diff options
Diffstat (limited to 'compiler/optimizing/nodes.h')
| -rw-r--r-- | compiler/optimizing/nodes.h | 1231 |
1 files changed, 896 insertions, 335 deletions
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 04c3963675..f09e958d29 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -17,6 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_NODES_H_ #define ART_COMPILER_OPTIMIZING_NODES_H_ +#include <type_traits> + #include "base/arena_containers.h" #include "base/arena_object.h" #include "dex/compiler_enums.h" @@ -38,6 +40,7 @@ class HBasicBlock; class HCurrentMethod; class HDoubleConstant; class HEnvironment; +class HFakeString; class HFloatConstant; class HGraphBuilder; class HGraphVisitor; @@ -48,6 +51,7 @@ class HLongConstant; class HNullConstant; class HPhi; class HSuspendCheck; +class HTryBoundary; class LiveInterval; class LocationSummary; class SlowPathCode; @@ -56,6 +60,7 @@ class SsaBuilder; static const int kDefaultNumberOfBlocks = 8; static const int kDefaultNumberOfSuccessors = 2; static const int kDefaultNumberOfPredecessors = 2; +static const int kDefaultNumberOfExceptionalPredecessors = 0; static const int kDefaultNumberOfDominatedBlocks = 1; static const int kDefaultNumberOfBackEdges = 1; @@ -181,6 +186,10 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { // visit for eliminating dead phis: a dead phi can only have loop header phi // users remaining when being visited. if (!AnalyzeNaturalLoops()) return false; + // Precompute per-block try membership before entering the SSA builder, + // which needs the information to build catch block phis from values of + // locals at throwing instructions inside try blocks. + ComputeTryBlockInformation(); TransformToSsa(); in_ssa_form_ = true; return true; @@ -192,14 +201,21 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { void BuildDominatorTree(); void TransformToSsa(); void SimplifyCFG(); + void SimplifyCatchBlocks(); // Analyze all natural loops in this graph. Returns false if one // loop is not natural, that is the header does not dominate the // back edge. bool AnalyzeNaturalLoops() const; + // Iterate over blocks to compute try block membership. Needs reverse post + // order and loop information. + void ComputeTryBlockInformation(); + // Inline this graph in `outer_graph`, replacing the given `invoke` instruction. - void InlineInto(HGraph* outer_graph, HInvoke* invoke); + // Returns the instruction used to replace the invoke expression or null if the + // invoke is for a void method. + HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke); // Need to add a couple of blocks to test if the loop body is entered and // put deoptimization instructions, etc. @@ -295,7 +311,12 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { // already, it is created and inserted into the graph. This method is only for // integral types. HConstant* GetConstant(Primitive::Type type, int64_t value); + + // TODO: This is problematic for the consistency of reference type propagation + // because it can be created anytime after the pass and thus it will be left + // with an invalid type. HNullConstant* GetNullConstant(); + HIntConstant* GetIntConstant(int32_t value) { return CreateConstant(value, &cached_int_constants_); } @@ -325,6 +346,10 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { return invoke_type_; } + InstructionSet GetInstructionSet() const { + return instruction_set_; + } + private: void VisitBlockForDominatorTree(HBasicBlock* block, HBasicBlock* predecessor, @@ -725,8 +750,11 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { return GetPredecessorIndexOf(predecessor) == idx; } - // Returns whether successor at index `idx` is an exception handler. - bool IsExceptionalSuccessor(size_t idx) const; + // Returns the number of non-exceptional successors. SsaChecker ensures that + // these are stored at the beginning of the successor list. + size_t NumberOfNormalSuccessors() const { + return EndsWithTryBoundary() ? 1 : GetSuccessors().Size(); + } // Split the block into two blocks just before `cursor`. Returns the newly // created, latter block. Note that this method will add the block to the @@ -825,6 +853,15 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { bool IsInLoop() const { return loop_information_ != nullptr; } + HTryBoundary* GetTryEntry() const { return try_entry_; } + void SetTryEntry(HTryBoundary* try_entry) { try_entry_ = try_entry; } + bool IsInTry() const { return try_entry_ != nullptr; } + + // Returns the try entry that this block's successors should have. They will + // be in the same try, unless the block ends in a try boundary. In that case, + // the appropriate try entry will be returned. + HTryBoundary* ComputeTryEntryOfSuccessors() const; + // Returns whether this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; @@ -841,6 +878,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { bool EndsWithControlFlowInstruction() const; bool EndsWithIf() const; + bool EndsWithTryBoundary() const; bool HasSinglePhi() const; private: @@ -859,6 +897,10 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { size_t lifetime_end_; bool is_catch_block_; + // If this block is in a try block, `try_entry_` stores one of, possibly + // several, TryBoundary instructions entering it. + HTryBoundary* try_entry_; + friend class HGraph; friend class HInstruction; @@ -900,6 +942,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(BoundsCheck, Instruction) \ M(BoundType, Instruction) \ M(CheckCast, Instruction) \ + M(ClearException, Instruction) \ M(ClinitCheck, Instruction) \ M(Compare, BinaryOperation) \ M(Condition, BinaryOperation) \ @@ -910,6 +953,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(DoubleConstant, Constant) \ M(Equal, Condition) \ M(Exit, Instruction) \ + M(FakeString, Instruction) \ M(FloatConstant, Constant) \ M(Goto, Instruction) \ M(GreaterThan, Condition) \ @@ -1149,13 +1193,35 @@ class HUserRecord : public ValueObject { HUseListNode<T>* use_node_; }; -// TODO: Add better documentation to this class and maybe refactor with more suggestive names. -// - Has(All)SideEffects suggests that all the side effects are present but only ChangesSomething -// flag is consider. -// - DependsOn suggests that there is a real dependency between side effects but it only -// checks DependendsOnSomething flag. -// -// Represents the side effects an instruction may have. +/** + * Side-effects representation. + * + * For write/read dependences on fields/arrays, the dependence analysis uses + * type disambiguation (e.g. a float field write cannot modify the value of an + * integer field read) and the access type (e.g. a reference array write cannot + * modify the value of a reference field read [although it may modify the + * reference fetch prior to reading the field, which is represented by its own + * write/read dependence]). The analysis makes conservative points-to + * assumptions on reference types (e.g. two same typed arrays are assumed to be + * the same, and any reference read depends on any reference read without + * further regard of its type). + * + * The internal representation uses 38-bit and is described in the table below. + * The first line indicates the side effect, and for field/array accesses the + * second line indicates the type of the access (in the order of the + * Primitive::Type enum). + * The two numbered lines below indicate the bit position in the bitfield (read + * vertically). + * + * |Depends on GC|ARRAY-R |FIELD-R |Can trigger GC|ARRAY-W |FIELD-W | + * +-------------+---------+---------+--------------+---------+---------+ + * | |DFJISCBZL|DFJISCBZL| |DFJISCBZL|DFJISCBZL| + * | 3 |333333322|222222221| 1 |111111110|000000000| + * | 7 |654321098|765432109| 8 |765432109|876543210| + * + * Note that, to ease the implementation, 'changes' bits are least significant + * bits, while 'dependency' bits are most significant bits. + */ class SideEffects : public ValueObject { public: SideEffects() : flags_(0) {} @@ -1165,57 +1231,204 @@ class SideEffects : public ValueObject { } static SideEffects All() { - return SideEffects(ChangesSomething().flags_ | DependsOnSomething().flags_); + return SideEffects(kAllChangeBits | kAllDependOnBits); + } + + static SideEffects AllChanges() { + return SideEffects(kAllChangeBits); + } + + static SideEffects AllDependencies() { + return SideEffects(kAllDependOnBits); + } + + static SideEffects AllExceptGCDependency() { + return AllWritesAndReads().Union(SideEffects::CanTriggerGC()); + } + + static SideEffects AllWritesAndReads() { + return SideEffects(kAllWrites | kAllReads); + } + + static SideEffects AllWrites() { + return SideEffects(kAllWrites); + } + + static SideEffects AllReads() { + return SideEffects(kAllReads); } - static SideEffects ChangesSomething() { - return SideEffects((1 << kFlagChangesCount) - 1); + static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) { + return is_volatile + ? AllWritesAndReads() + : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset)); } - static SideEffects DependsOnSomething() { - int count = kFlagDependsOnCount - kFlagChangesCount; - return SideEffects(((1 << count) - 1) << kFlagChangesCount); + static SideEffects ArrayWriteOfType(Primitive::Type type) { + return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset)); } + static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) { + return is_volatile + ? AllWritesAndReads() + : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset)); + } + + static SideEffects ArrayReadOfType(Primitive::Type type) { + return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset)); + } + + static SideEffects CanTriggerGC() { + return SideEffects(1ULL << kCanTriggerGCBit); + } + + static SideEffects DependsOnGC() { + return SideEffects(1ULL << kDependsOnGCBit); + } + + // Combines the side-effects of this and the other. SideEffects Union(SideEffects other) const { return SideEffects(flags_ | other.flags_); } - bool HasSideEffects() const { - size_t all_bits_set = (1 << kFlagChangesCount) - 1; - return (flags_ & all_bits_set) != 0; + SideEffects Exclusion(SideEffects other) const { + return SideEffects(flags_ & ~other.flags_); } - bool HasAllSideEffects() const { - size_t all_bits_set = (1 << kFlagChangesCount) - 1; - return all_bits_set == (flags_ & all_bits_set); + bool Includes(SideEffects other) const { + return (other.flags_ & flags_) == other.flags_; } - bool DependsOn(SideEffects other) const { - size_t depends_flags = other.ComputeDependsFlags(); - return (flags_ & depends_flags) != 0; + bool HasSideEffects() const { + return (flags_ & kAllChangeBits); } bool HasDependencies() const { - int count = kFlagDependsOnCount - kFlagChangesCount; - size_t all_bits_set = (1 << count) - 1; - return ((flags_ >> kFlagChangesCount) & all_bits_set) != 0; + return (flags_ & kAllDependOnBits); + } + + // Returns true if there are no side effects or dependencies. + bool DoesNothing() const { + return flags_ == 0; + } + + // Returns true if something is written. + bool DoesAnyWrite() const { + return (flags_ & kAllWrites); } + // Returns true if something is read. + bool DoesAnyRead() const { + return (flags_ & kAllReads); + } + + // Returns true if potentially everything is written and read + // (every type and every kind of access). + bool DoesAllReadWrite() const { + return (flags_ & (kAllWrites | kAllReads)) == (kAllWrites | kAllReads); + } + + bool DoesAll() const { + return flags_ == (kAllChangeBits | kAllDependOnBits); + } + + // Returns true if this may read something written by other. + bool MayDependOn(SideEffects other) const { + const uint64_t depends_on_flags = (flags_ & kAllDependOnBits) >> kChangeBits; + return (other.flags_ & depends_on_flags); + } + + // Returns string representation of flags (for debugging only). + // Format: |x|DFJISCBZL|DFJISCBZL|y|DFJISCBZL|DFJISCBZL| + std::string ToString() const { + std::string flags = "|"; + for (int s = kLastBit; s >= 0; s--) { + bool current_bit_is_set = ((flags_ >> s) & 1) != 0; + if ((s == kDependsOnGCBit) || (s == kCanTriggerGCBit)) { + // This is a bit for the GC side effect. + if (current_bit_is_set) { + flags += "GC"; + } + flags += "|"; + } else { + // This is a bit for the array/field analysis. + // The underscore character stands for the 'can trigger GC' bit. + static const char *kDebug = "LZBCSIJFDLZBCSIJFD_LZBCSIJFDLZBCSIJFD"; + if (current_bit_is_set) { + flags += kDebug[s]; + } + if ((s == kFieldWriteOffset) || (s == kArrayWriteOffset) || + (s == kFieldReadOffset) || (s == kArrayReadOffset)) { + flags += "|"; + } + } + } + return flags; + } + + bool Equals(const SideEffects& other) const { return flags_ == other.flags_; } + private: - static constexpr int kFlagChangesSomething = 0; - static constexpr int kFlagChangesCount = kFlagChangesSomething + 1; + static constexpr int kFieldArrayAnalysisBits = 9; + + static constexpr int kFieldWriteOffset = 0; + static constexpr int kArrayWriteOffset = kFieldWriteOffset + kFieldArrayAnalysisBits; + static constexpr int kLastBitForWrites = kArrayWriteOffset + kFieldArrayAnalysisBits - 1; + static constexpr int kCanTriggerGCBit = kLastBitForWrites + 1; + + static constexpr int kChangeBits = kCanTriggerGCBit + 1; + + static constexpr int kFieldReadOffset = kCanTriggerGCBit + 1; + static constexpr int kArrayReadOffset = kFieldReadOffset + kFieldArrayAnalysisBits; + static constexpr int kLastBitForReads = kArrayReadOffset + kFieldArrayAnalysisBits - 1; + static constexpr int kDependsOnGCBit = kLastBitForReads + 1; + + static constexpr int kLastBit = kDependsOnGCBit; + static constexpr int kDependOnBits = kLastBit + 1 - kChangeBits; - static constexpr int kFlagDependsOnSomething = kFlagChangesCount; - static constexpr int kFlagDependsOnCount = kFlagDependsOnSomething + 1; + // Aliases. - explicit SideEffects(size_t flags) : flags_(flags) {} + static_assert(kChangeBits == kDependOnBits, + "the 'change' bits should match the 'depend on' bits."); - size_t ComputeDependsFlags() const { - return flags_ << kFlagChangesCount; + static constexpr uint64_t kAllChangeBits = ((1ULL << kChangeBits) - 1); + static constexpr uint64_t kAllDependOnBits = ((1ULL << kDependOnBits) - 1) << kChangeBits; + static constexpr uint64_t kAllWrites = + ((1ULL << (kLastBitForWrites + 1 - kFieldWriteOffset)) - 1) << kFieldWriteOffset; + static constexpr uint64_t kAllReads = + ((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset; + + // Work around the fact that HIR aliases I/F and J/D. + // TODO: remove this interceptor once HIR types are clean + static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) { + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + return TypeFlag(Primitive::kPrimInt, offset) | + TypeFlag(Primitive::kPrimFloat, offset); + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + return TypeFlag(Primitive::kPrimLong, offset) | + TypeFlag(Primitive::kPrimDouble, offset); + default: + return TypeFlag(type, offset); + } } - size_t flags_; + // Translates type to bit flag. + static uint64_t TypeFlag(Primitive::Type type, int offset) { + CHECK_NE(type, Primitive::kPrimVoid); + const uint64_t one = 1; + const int shift = type; // 0-based consecutive enum + DCHECK_LE(kFieldWriteOffset, shift); + DCHECK_LT(shift, kArrayWriteOffset); + return one << (type + offset); + } + + // Private constructor on direct flags value. + explicit SideEffects(uint64_t flags) : flags_(flags) {} + + uint64_t flags_; }; // A HEnvironment object contains the values of virtual registers at a given location. @@ -1335,8 +1548,7 @@ class HEnvironment : public ArenaObject<kArenaAllocMisc> { const uint32_t dex_pc_; const InvokeType invoke_type_; - // The instruction that holds this environment. Only used in debug mode - // to ensure the graph is consistent. + // The instruction that holds this environment. HInstruction* const holder_; friend class HInstruction; @@ -1348,79 +1560,64 @@ class ReferenceTypeInfo : ValueObject { public: typedef Handle<mirror::Class> TypeHandle; - static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (type_handle->IsObjectClass()) { - // Override the type handle to be consistent with the case when we get to - // Top but don't have the Object class available. It avoids having to guess - // what value the type_handle has when it's Top. - return ReferenceTypeInfo(TypeHandle(), is_exact, true); - } else { - return ReferenceTypeInfo(type_handle, is_exact, false); - } + static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { + // The constructor will check that the type_handle is valid. + return ReferenceTypeInfo(type_handle, is_exact); } - static ReferenceTypeInfo CreateTop(bool is_exact) { - return ReferenceTypeInfo(TypeHandle(), is_exact, true); + static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); } + + static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) { + return handle.GetReference() != nullptr; } + bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) { + return IsValidHandle(type_handle_); + } bool IsExact() const { return is_exact_; } - bool IsTop() const { return is_top_; } - bool IsInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return !IsTop() && GetTypeHandle()->IsInterface(); + + bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsObjectClass(); + } + bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsInterface(); } Handle<mirror::Class> GetTypeHandle() const { return type_handle_; } - bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (IsTop()) { - // Top (equivalent for java.lang.Object) is supertype of anything. - return true; - } - if (rti.IsTop()) { - // If we get here `this` is not Top() so it can't be a supertype. - return false; - } + bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + DCHECK(rti.IsValid()); return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get()); } // Returns true if the type information provide the same amount of details. // Note that it does not mean that the instructions have the same actual type - // (e.g. tops are equal but they can be the result of a merge). - bool IsEqual(ReferenceTypeInfo rti) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (IsExact() != rti.IsExact()) { - return false; - } - if (IsTop() && rti.IsTop()) { - // `Top` means java.lang.Object, so the types are equivalent. + // (because the type can be the result of a merge). + bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) { + if (!IsValid() && !rti.IsValid()) { + // Invalid types are equal. return true; } - if (IsTop() || rti.IsTop()) { - // If only one is top or object than they are not equivalent. - // NB: We need this extra check because the type_handle of `Top` is invalid - // and we cannot inspect its reference. + if (!IsValid() || !rti.IsValid()) { + // One is valid, the other not. return false; } - - // Finally check the types. - return GetTypeHandle().Get() == rti.GetTypeHandle().Get(); + return IsExact() == rti.IsExact() + && GetTypeHandle().Get() == rti.GetTypeHandle().Get(); } private: - ReferenceTypeInfo() : ReferenceTypeInfo(TypeHandle(), false, true) {} - ReferenceTypeInfo(TypeHandle type_handle, bool is_exact, bool is_top) - : type_handle_(type_handle), is_exact_(is_exact), is_top_(is_top) {} + ReferenceTypeInfo(); + ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); // The class of the object. TypeHandle type_handle_; // Whether or not the type is exact or a superclass of the actual type. // Whether or not we have any information about this type. bool is_exact_; - // A true value here means that the object type should be java.lang.Object. - // We don't have access to the corresponding mirror object every time so this - // flag acts as a substitute. When true, the TypeHandle refers to a null - // pointer and should not be used. - bool is_top_; }; std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs); @@ -1438,7 +1635,7 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { live_interval_(nullptr), lifetime_position_(kNoLifetime), side_effects_(side_effects), - reference_type_info_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {} + reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {} virtual ~HInstruction() {} @@ -1455,6 +1652,7 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { HInstruction* GetPreviousDisregardingMoves() const; HBasicBlock* GetBlock() const { return block_; } + ArenaAllocator* GetArena() const { return block_->GetGraph()->GetArena(); } void SetBlock(HBasicBlock* block) { block_ = block; } bool IsInBlock() const { return block_ != nullptr; } bool IsInLoop() const { return block_->IsInLoop(); } @@ -1479,10 +1677,13 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { } virtual bool IsControlFlow() const { return false; } virtual bool CanThrow() const { return false; } + bool HasSideEffects() const { return side_effects_.HasSideEffects(); } + bool DoesAnyWrite() const { return side_effects_.DoesAnyWrite(); } // Does not apply for all instructions, but having this at top level greatly // simplifies the null check elimination. + // TODO: Consider merging can_be_null into ReferenceTypeInfo. virtual bool CanBeNull() const { DCHECK_EQ(GetType(), Primitive::kPrimNot) << "CanBeNull only applies to reference types"; return true; @@ -1493,10 +1694,7 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { return false; } - void SetReferenceTypeInfo(ReferenceTypeInfo reference_type_info) { - DCHECK_EQ(GetType(), Primitive::kPrimNot); - reference_type_info_ = reference_type_info; - } + void SetReferenceTypeInfo(ReferenceTypeInfo rti); ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); @@ -1659,6 +1857,14 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { virtual bool NeedsDexCache() const { return false; } + // Does this instruction have any use in an environment before + // control flow hits 'other'? + bool HasAnyEnvironmentUseBefore(HInstruction* other); + + // Remove all references to environment uses of this instruction. + // The caller must ensure that this is safe to do. + void RemoveEnvironmentUsers(); + protected: virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0; virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0; @@ -1914,6 +2120,95 @@ class HGoto : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HGoto); }; +class HConstant : public HExpression<0> { + public: + explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {} + + bool CanBeMoved() const OVERRIDE { return true; } + + virtual bool IsMinusOne() const { return false; } + virtual bool IsZero() const { return false; } + virtual bool IsOne() const { return false; } + + DECLARE_INSTRUCTION(Constant); + + private: + DISALLOW_COPY_AND_ASSIGN(HConstant); +}; + +class HNullConstant : public HConstant { + public: + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } + + size_t ComputeHashCode() const OVERRIDE { return 0; } + + DECLARE_INSTRUCTION(NullConstant); + + private: + HNullConstant() : HConstant(Primitive::kPrimNot) {} + + friend class HGraph; + DISALLOW_COPY_AND_ASSIGN(HNullConstant); +}; + +// Constants of the type int. Those can be from Dex instructions, or +// synthesized (for example with the if-eqz instruction). +class HIntConstant : public HConstant { + public: + int32_t GetValue() const { return value_; } + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + DCHECK(other->IsIntConstant()); + return other->AsIntConstant()->value_ == value_; + } + + size_t ComputeHashCode() const OVERRIDE { return GetValue(); } + + bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } + bool IsZero() const OVERRIDE { return GetValue() == 0; } + bool IsOne() const OVERRIDE { return GetValue() == 1; } + + DECLARE_INSTRUCTION(IntConstant); + + private: + explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {} + explicit HIntConstant(bool value) : HConstant(Primitive::kPrimInt), value_(value ? 1 : 0) {} + + const int32_t value_; + + friend class HGraph; + ART_FRIEND_TEST(GraphTest, InsertInstructionBefore); + ART_FRIEND_TYPED_TEST(ParallelMoveTest, ConstantLast); + DISALLOW_COPY_AND_ASSIGN(HIntConstant); +}; + +class HLongConstant : public HConstant { + public: + int64_t GetValue() const { return value_; } + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + DCHECK(other->IsLongConstant()); + return other->AsLongConstant()->value_ == value_; + } + + size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } + + bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } + bool IsZero() const OVERRIDE { return GetValue() == 0; } + bool IsOne() const OVERRIDE { return GetValue() == 1; } + + DECLARE_INSTRUCTION(LongConstant); + + private: + explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {} + + const int64_t value_; + + friend class HGraph; + DISALLOW_COPY_AND_ASSIGN(HLongConstant); +}; // Conditional branch. A block ending with an HIf instruction must have // two successors. @@ -1962,29 +2257,24 @@ class HTryBoundary : public HTemplateInstruction<0> { // Returns whether `handler` is among its exception handlers (non-zero index // successors). - bool HasExceptionHandler(HBasicBlock* handler) const { - DCHECK(handler->IsCatchBlock()); - return GetBlock()->GetSuccessors().Contains(handler, /* start_from */ 1); - } - - // Returns whether successor at index `idx` is an exception handler. - bool IsExceptionalSuccessor(size_t idx) const { - DCHECK_LT(idx, GetBlock()->GetSuccessors().Size()); - bool is_handler = (idx != 0); - DCHECK(!is_handler || GetBlock()->GetSuccessors().Get(idx)->IsCatchBlock()); - return is_handler; + bool HasExceptionHandler(const HBasicBlock& handler) const { + DCHECK(handler.IsCatchBlock()); + return GetBlock()->GetSuccessors().Contains( + const_cast<HBasicBlock*>(&handler), /* start_from */ 1); } // If not present already, adds `handler` to its block's list of exception // handlers. void AddExceptionHandler(HBasicBlock* handler) { - if (!HasExceptionHandler(handler)) { + if (!HasExceptionHandler(*handler)) { GetBlock()->AddSuccessor(handler); } } bool IsEntry() const { return kind_ == BoundaryKind::kEntry; } + bool HasSameExceptionHandlersAs(const HTryBoundary& other) const; + DECLARE_INSTRUCTION(TryBoundary); private: @@ -1993,6 +2283,24 @@ class HTryBoundary : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; +// Iterator over exception handlers of a given HTryBoundary, i.e. over +// exceptional successors of its basic block. +class HExceptionHandlerIterator : public ValueObject { + public: + explicit HExceptionHandlerIterator(const HTryBoundary& try_boundary) + : block_(*try_boundary.GetBlock()), index_(block_.NumberOfNormalSuccessors()) {} + + bool Done() const { return index_ == block_.GetSuccessors().Size(); } + HBasicBlock* Current() const { return block_.GetSuccessors().Get(index_); } + size_t CurrentSuccessorIndex() const { return index_; } + void Advance() { ++index_; } + + private: + const HBasicBlock& block_; + size_t index_; + + DISALLOW_COPY_AND_ASSIGN(HExceptionHandlerIterator); +}; // Deoptimize to interpreter, upon checking a condition. class HDeoptimize : public HTemplateInstruction<1> { @@ -2050,8 +2358,8 @@ class HUnaryOperation : public HExpression<1> { HConstant* TryStaticEvaluation() const; // Apply this operation to `x`. - virtual int32_t Evaluate(int32_t x) const = 0; - virtual int64_t Evaluate(int64_t x) const = 0; + virtual HConstant* Evaluate(HIntConstant* x) const = 0; + virtual HConstant* Evaluate(HLongConstant* x) const = 0; DECLARE_INSTRUCTION(UnaryOperation); @@ -2063,7 +2371,9 @@ class HBinaryOperation : public HExpression<2> { public: HBinaryOperation(Primitive::Type result_type, HInstruction* left, - HInstruction* right) : HExpression(result_type, SideEffects::None()) { + HInstruction* right, + SideEffects side_effects = SideEffects::None()) + : HExpression(result_type, side_effects) { SetRawInputAt(0, left); SetRawInputAt(1, right); } @@ -2118,8 +2428,18 @@ class HBinaryOperation : public HExpression<2> { HConstant* TryStaticEvaluation() const; // Apply this operation to `x` and `y`. - virtual int32_t Evaluate(int32_t x, int32_t y) const = 0; - virtual int64_t Evaluate(int64_t x, int64_t y) const = 0; + virtual HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const = 0; + virtual HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const = 0; + virtual HConstant* Evaluate(HIntConstant* x ATTRIBUTE_UNUSED, + HLongConstant* y ATTRIBUTE_UNUSED) const { + VLOG(compiler) << DebugName() << " is not defined for the (int, long) case."; + return nullptr; + } + virtual HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED, + HIntConstant* y ATTRIBUTE_UNUSED) const { + VLOG(compiler) << DebugName() << " is not defined for the (long, int) case."; + return nullptr; + } // Returns an input that can legally be used as the right input and is // constant, or null. @@ -2135,11 +2455,20 @@ class HBinaryOperation : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HBinaryOperation); }; +// The comparison bias applies for floating point operations and indicates how NaN +// comparisons are treated: +enum class ComparisonBias { + kNoBias, // bias is not applicable (i.e. for long operation) + kGtBias, // return 1 for NaN comparisons + kLtBias, // return -1 for NaN comparisons +}; + class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second) : HBinaryOperation(Primitive::kPrimBoolean, first, second), - needs_materialization_(true) {} + needs_materialization_(true), + bias_(ComparisonBias::kNoBias) {} bool NeedsMaterialization() const { return needs_materialization_; } void ClearNeedsMaterialization() { needs_materialization_ = false; } @@ -2152,11 +2481,36 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetCondition() const = 0; + virtual IfCondition GetOppositeCondition() const = 0; + + bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } + + void SetBias(ComparisonBias bias) { bias_ = bias; } + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return bias_ == other->AsCondition()->bias_; + } + + bool IsFPConditionTrueIfNaN() const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())); + IfCondition if_cond = GetCondition(); + return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE); + } + + bool IsFPConditionFalseIfNaN() const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())); + IfCondition if_cond = GetCondition(); + return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ); + } + private: // For register allocation purposes, returns whether this instruction needs to be // materialized (that is, not just be in the processor flags). bool needs_materialization_; + // Needed if we merge a HCompare into a HCondition. + ComparisonBias bias_; + DISALLOW_COPY_AND_ASSIGN(HCondition); }; @@ -2168,11 +2522,13 @@ class HEqual : public HCondition { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x == y ? 1 : 0; + template <typename T> bool Compute(T x, T y) const { return x == y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x == y ? 1 : 0; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(Equal); @@ -2181,6 +2537,10 @@ class HEqual : public HCondition { return kCondEQ; } + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondNE; + } + private: DISALLOW_COPY_AND_ASSIGN(HEqual); }; @@ -2192,11 +2552,13 @@ class HNotEqual : public HCondition { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x != y ? 1 : 0; + template <typename T> bool Compute(T x, T y) const { return x != y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x != y ? 1 : 0; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(NotEqual); @@ -2205,6 +2567,10 @@ class HNotEqual : public HCondition { return kCondNE; } + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondEQ; + } + private: DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; @@ -2214,11 +2580,13 @@ class HLessThan : public HCondition { HLessThan(HInstruction* first, HInstruction* second) : HCondition(first, second) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x < y ? 1 : 0; + template <typename T> bool Compute(T x, T y) const { return x < y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x < y ? 1 : 0; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(LessThan); @@ -2227,6 +2595,10 @@ class HLessThan : public HCondition { return kCondLT; } + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondGE; + } + private: DISALLOW_COPY_AND_ASSIGN(HLessThan); }; @@ -2236,11 +2608,13 @@ class HLessThanOrEqual : public HCondition { HLessThanOrEqual(HInstruction* first, HInstruction* second) : HCondition(first, second) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x <= y ? 1 : 0; + template <typename T> bool Compute(T x, T y) const { return x <= y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x <= y ? 1 : 0; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(LessThanOrEqual); @@ -2249,6 +2623,10 @@ class HLessThanOrEqual : public HCondition { return kCondLE; } + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondGT; + } + private: DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; @@ -2258,11 +2636,13 @@ class HGreaterThan : public HCondition { HGreaterThan(HInstruction* first, HInstruction* second) : HCondition(first, second) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x > y ? 1 : 0; + template <typename T> bool Compute(T x, T y) const { return x > y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x > y ? 1 : 0; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(GreaterThan); @@ -2271,6 +2651,10 @@ class HGreaterThan : public HCondition { return kCondGT; } + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondLE; + } + private: DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; @@ -2280,11 +2664,13 @@ class HGreaterThanOrEqual : public HCondition { HGreaterThanOrEqual(HInstruction* first, HInstruction* second) : HCondition(first, second) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x >= y ? 1 : 0; + template <typename T> bool Compute(T x, T y) const { return x >= y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x >= y ? 1 : 0; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(GreaterThanOrEqual); @@ -2293,6 +2679,10 @@ class HGreaterThanOrEqual : public HCondition { return kCondGE; } + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondLT; + } + private: DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; @@ -2302,50 +2692,47 @@ class HGreaterThanOrEqual : public HCondition { // Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1. class HCompare : public HBinaryOperation { public: - // The bias applies for floating point operations and indicates how NaN - // comparisons are treated: - enum Bias { - kNoBias, // bias is not applicable (i.e. for long operation) - kGtBias, // return 1 for NaN comparisons - kLtBias, // return -1 for NaN comparisons - }; - HCompare(Primitive::Type type, HInstruction* first, HInstruction* second, - Bias bias, + ComparisonBias bias, uint32_t dex_pc) - : HBinaryOperation(Primitive::kPrimInt, first, second), bias_(bias), dex_pc_(dex_pc) { + : HBinaryOperation(Primitive::kPrimInt, first, second, SideEffectsForArchRuntimeCalls(type)), + bias_(bias), + dex_pc_(dex_pc) { DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return - x == y ? 0 : - x > y ? 1 : - -1; - } + template <typename T> + int32_t Compute(T x, T y) const { return x == y ? 0 : x > y ? 1 : -1; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return - x == y ? 0 : - x > y ? 1 : - -1; + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { return bias_ == other->AsCompare()->bias_; } - bool IsGtBias() { return bias_ == kGtBias; } + ComparisonBias GetBias() const { return bias_; } - uint32_t GetDexPc() const { return dex_pc_; } + bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; } + + uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } + + static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) { + // MIPS64 uses a runtime call for FP comparisons. + return Primitive::IsFloatingPointType(type) ? SideEffects::CanTriggerGC() : SideEffects::None(); + } DECLARE_INSTRUCTION(Compare); private: - const Bias bias_; + const ComparisonBias bias_; const uint32_t dex_pc_; DISALLOW_COPY_AND_ASSIGN(HCompare); @@ -2401,27 +2788,12 @@ class HStoreLocal : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HStoreLocal); }; -class HConstant : public HExpression<0> { - public: - explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {} - - bool CanBeMoved() const OVERRIDE { return true; } - - virtual bool IsMinusOne() const { return false; } - virtual bool IsZero() const { return false; } - virtual bool IsOne() const { return false; } - - DECLARE_INSTRUCTION(Constant); - - private: - DISALLOW_COPY_AND_ASSIGN(HConstant); -}; - class HFloatConstant : public HConstant { public: float GetValue() const { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + DCHECK(other->IsFloatConstant()); return bit_cast<uint32_t, float>(other->AsFloatConstant()->value_) == bit_cast<uint32_t, float>(value_); } @@ -2461,6 +2833,7 @@ class HDoubleConstant : public HConstant { double GetValue() const { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + DCHECK(other->IsDoubleConstant()); return bit_cast<uint64_t, double>(other->AsDoubleConstant()->value_) == bit_cast<uint64_t, double>(value_); } @@ -2495,79 +2868,8 @@ class HDoubleConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); }; -class HNullConstant : public HConstant { - public: - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; - } - - size_t ComputeHashCode() const OVERRIDE { return 0; } - - DECLARE_INSTRUCTION(NullConstant); - - private: - HNullConstant() : HConstant(Primitive::kPrimNot) {} - - friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HNullConstant); -}; - -// Constants of the type int. Those can be from Dex instructions, or -// synthesized (for example with the if-eqz instruction). -class HIntConstant : public HConstant { - public: - int32_t GetValue() const { return value_; } - - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return other->AsIntConstant()->value_ == value_; - } - - size_t ComputeHashCode() const OVERRIDE { return GetValue(); } - - bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } - bool IsZero() const OVERRIDE { return GetValue() == 0; } - bool IsOne() const OVERRIDE { return GetValue() == 1; } - - DECLARE_INSTRUCTION(IntConstant); - - private: - explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {} - - const int32_t value_; - - friend class HGraph; - ART_FRIEND_TEST(GraphTest, InsertInstructionBefore); - ART_FRIEND_TYPED_TEST(ParallelMoveTest, ConstantLast); - DISALLOW_COPY_AND_ASSIGN(HIntConstant); -}; - -class HLongConstant : public HConstant { - public: - int64_t GetValue() const { return value_; } - - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return other->AsLongConstant()->value_ == value_; - } - - size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } - - bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } - bool IsZero() const OVERRIDE { return GetValue() == 0; } - bool IsOne() const OVERRIDE { return GetValue() == 1; } - - DECLARE_INSTRUCTION(LongConstant); - - private: - explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {} - - const int64_t value_; - - friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HLongConstant); -}; - enum class Intrinsics { -#define OPTIMIZING_INTRINSICS(Name, IsStatic) k ## Name, +#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironment) k ## Name, #include "intrinsics_list.h" kNone, INTRINSICS_LIST(OPTIMIZING_INTRINSICS) @@ -2576,13 +2878,18 @@ enum class Intrinsics { }; std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic); +enum IntrinsicNeedsEnvironment { + kNoEnvironment, // Intrinsic does not require an environment. + kNeedsEnvironment // Intrinsic requires an environment. +}; + class HInvoke : public HInstruction { public: size_t InputCount() const OVERRIDE { return inputs_.Size(); } // Runtime needs to walk the stack, so Dex -> Dex calls need to // know their environment. - bool NeedsEnvironment() const OVERRIDE { return true; } + bool NeedsEnvironment() const OVERRIDE { return needs_environment_ == kNeedsEnvironment; } void SetArgumentAt(size_t index, HInstruction* argument) { SetRawInputAt(index, argument); @@ -2607,8 +2914,9 @@ class HInvoke : public HInstruction { return intrinsic_; } - void SetIntrinsic(Intrinsics intrinsic) { + void SetIntrinsic(Intrinsics intrinsic, IntrinsicNeedsEnvironment needs_environment) { intrinsic_ = intrinsic; + needs_environment_ = needs_environment; } bool IsFromInlinedInvoke() const { @@ -2627,14 +2935,16 @@ class HInvoke : public HInstruction { uint32_t dex_pc, uint32_t dex_method_index, InvokeType original_invoke_type) - : HInstruction(SideEffects::All()), + : HInstruction( + SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays. number_of_arguments_(number_of_arguments), inputs_(arena, number_of_arguments), return_type_(return_type), dex_pc_(dex_pc), dex_method_index_(dex_method_index), original_invoke_type_(original_invoke_type), - intrinsic_(Intrinsics::kNone) { + intrinsic_(Intrinsics::kNone), + needs_environment_(kNeedsEnvironment) { uint32_t number_of_inputs = number_of_arguments + number_of_other_inputs; inputs_.SetSize(number_of_inputs); } @@ -2651,6 +2961,7 @@ class HInvoke : public HInstruction { const uint32_t dex_method_index_; const InvokeType original_invoke_type_; Intrinsics intrinsic_; + IntrinsicNeedsEnvironment needs_environment_; private: DISALLOW_COPY_AND_ASSIGN(HInvoke); @@ -2678,9 +2989,11 @@ class HInvokeStaticOrDirect : public HInvoke { ClinitCheckRequirement clinit_check_requirement) : HInvoke(arena, number_of_arguments, - // There is one extra argument for the HCurrentMethod node, and - // potentially one other if the clinit check is explicit. - clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 2u : 1u, + // There is one extra argument for the HCurrentMethod node, and + // potentially one other if the clinit check is explicit, and one other + // if the method is a string factory. + 1u + (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u) + + (string_init_offset ? 1u : 0u), return_type, dex_pc, dex_method_index, @@ -2697,6 +3010,10 @@ class HInvokeStaticOrDirect : public HInvoke { return false; } + bool CanBeNull() const OVERRIDE { + return return_type_ == Primitive::kPrimNot && !IsStringInit(); + } + InvokeType GetInvokeType() const { return invoke_type_; } bool IsRecursive() const { return is_recursive_; } bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); } @@ -2725,6 +3042,23 @@ class HInvokeStaticOrDirect : public HInvoke { DCHECK(IsStaticWithImplicitClinitCheck()); } + bool IsStringFactoryFor(HFakeString* str) const { + if (!IsStringInit()) return false; + // +1 for the current method. + if (InputCount() == (number_of_arguments_ + 1)) return false; + return InputAt(InputCount() - 1)->AsFakeString() == str; + } + + void RemoveFakeStringArgumentAsLastInput() { + DCHECK(IsStringInit()); + size_t last_input_index = InputCount() - 1; + HInstruction* last_input = InputAt(last_input_index); + DCHECK(last_input != nullptr); + DCHECK(last_input->IsFakeString()) << last_input->DebugName(); + RemoveAsUserOfInput(last_input_index); + inputs_.DeleteAt(last_input_index); + } + // Is this a call to a static method whose declaring class has an // explicit intialization check in the graph? bool IsStaticWithExplicitClinitCheck() const { @@ -2825,7 +3159,7 @@ class HNewInstance : public HExpression<1> { uint16_t type_index, const DexFile& dex_file, QuickEntrypointEnum entrypoint) - : HExpression(Primitive::kPrimNot, SideEffects::None()), + : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()), dex_pc_(dex_pc), type_index_(type_index), dex_file_(dex_file), @@ -2862,11 +3196,17 @@ class HNewInstance : public HExpression<1> { class HNeg : public HUnaryOperation { public: - explicit HNeg(Primitive::Type result_type, HInstruction* input) + HNeg(Primitive::Type result_type, HInstruction* input) : HUnaryOperation(result_type, input) {} - int32_t Evaluate(int32_t x) const OVERRIDE { return -x; } - int64_t Evaluate(int64_t x) const OVERRIDE { return -x; } + template <typename T> T Compute(T x) const { return -x; } + + HConstant* Evaluate(HIntConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue())); + } + HConstant* Evaluate(HLongConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue())); + } DECLARE_INSTRUCTION(Neg); @@ -2882,7 +3222,7 @@ class HNewArray : public HExpression<2> { uint16_t type_index, const DexFile& dex_file, QuickEntrypointEnum entrypoint) - : HExpression(Primitive::kPrimNot, SideEffects::None()), + : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()), dex_pc_(dex_pc), type_index_(type_index), dex_file_(dex_file), @@ -2923,11 +3263,13 @@ class HAdd : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x + y; + template <typename T> T Compute(T x, T y) const { return x + y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x + y; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(Add); @@ -2941,11 +3283,13 @@ class HSub : public HBinaryOperation { HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HBinaryOperation(result_type, left, right) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - return x - y; + template <typename T> T Compute(T x, T y) const { return x - y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - return x - y; + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); } DECLARE_INSTRUCTION(Sub); @@ -2961,8 +3305,14 @@ class HMul : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x * y; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x * y; } + template <typename T> T Compute(T x, T y) const { return x * y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } DECLARE_INSTRUCTION(Mul); @@ -2973,23 +3323,32 @@ class HMul : public HBinaryOperation { class HDiv : public HBinaryOperation { public: HDiv(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc) - : HBinaryOperation(result_type, left, right), dex_pc_(dex_pc) {} + : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()), + dex_pc_(dex_pc) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - // Our graph structure ensures we never have 0 for `y` during constant folding. + template <typename T> + T Compute(T x, T y) const { + // Our graph structure ensures we never have 0 for `y` during + // constant folding. DCHECK_NE(y, 0); // Special case -1 to avoid getting a SIGFPE on x86(_64). return (y == -1) ? -x : x / y; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - DCHECK_NE(y, 0); - // Special case -1 to avoid getting a SIGFPE on x86(_64). - return (y == -1) ? -x : x / y; + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); } uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } + static SideEffects SideEffectsForArchRuntimeCalls() { + // The generated code can use a runtime call. + return SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(Div); private: @@ -3001,22 +3360,31 @@ class HDiv : public HBinaryOperation { class HRem : public HBinaryOperation { public: HRem(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc) - : HBinaryOperation(result_type, left, right), dex_pc_(dex_pc) {} + : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()), + dex_pc_(dex_pc) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { + template <typename T> + T Compute(T x, T y) const { + // Our graph structure ensures we never have 0 for `y` during + // constant folding. DCHECK_NE(y, 0); // Special case -1 to avoid getting a SIGFPE on x86(_64). return (y == -1) ? 0 : x % y; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - DCHECK_NE(y, 0); - // Special case -1 to avoid getting a SIGFPE on x86(_64). - return (y == -1) ? 0 : x % y; + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); } uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(Rem); private: @@ -3032,6 +3400,8 @@ class HDivZeroCheck : public HExpression<1> { SetRawInputAt(0, value); } + Primitive::Type GetType() const OVERRIDE { return InputAt(0)->GetType(); } + bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -3057,8 +3427,27 @@ class HShl : public HBinaryOperation { HShl(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HBinaryOperation(result_type, left, right) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x << (y & kMaxIntShiftValue); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x << (y & kMaxLongShiftValue); } + template <typename T, typename U, typename V> + T Compute(T x, U y, V max_shift_value) const { + static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, + "V is not the unsigned integer type corresponding to T"); + return x << (y & max_shift_value); + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue)); + } + // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this + // case is handled as `x << static_cast<int>(y)`. + HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue)); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue)); + } DECLARE_INSTRUCTION(Shl); @@ -3071,8 +3460,27 @@ class HShr : public HBinaryOperation { HShr(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HBinaryOperation(result_type, left, right) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x >> (y & kMaxIntShiftValue); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x >> (y & kMaxLongShiftValue); } + template <typename T, typename U, typename V> + T Compute(T x, U y, V max_shift_value) const { + static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, + "V is not the unsigned integer type corresponding to T"); + return x >> (y & max_shift_value); + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue)); + } + // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this + // case is handled as `x >> static_cast<int>(y)`. + HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue)); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue)); + } DECLARE_INSTRUCTION(Shr); @@ -3085,16 +3493,27 @@ class HUShr : public HBinaryOperation { HUShr(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HBinaryOperation(result_type, left, right) {} - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { - uint32_t ux = static_cast<uint32_t>(x); - uint32_t uy = static_cast<uint32_t>(y) & kMaxIntShiftValue; - return static_cast<int32_t>(ux >> uy); + template <typename T, typename U, typename V> + T Compute(T x, U y, V max_shift_value) const { + static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, + "V is not the unsigned integer type corresponding to T"); + V ux = static_cast<V>(x); + return static_cast<T>(ux >> (y & max_shift_value)); } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { - uint64_t ux = static_cast<uint64_t>(x); - uint64_t uy = static_cast<uint64_t>(y) & kMaxLongShiftValue; - return static_cast<int64_t>(ux >> uy); + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue)); + } + // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this + // case is handled as `x >>> static_cast<int>(y)`. + HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue)); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue)); } DECLARE_INSTRUCTION(UShr); @@ -3110,8 +3529,21 @@ class HAnd : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x & y; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x & y; } + template <typename T, typename U> + auto Compute(T x, U y) const -> decltype(x & y) { return x & y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } DECLARE_INSTRUCTION(And); @@ -3126,8 +3558,21 @@ class HOr : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x | y; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x | y; } + template <typename T, typename U> + auto Compute(T x, U y) const -> decltype(x | y) { return x | y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } DECLARE_INSTRUCTION(Or); @@ -3142,8 +3587,21 @@ class HXor : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x ^ y; } - int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x ^ y; } + template <typename T, typename U> + auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue())); + } DECLARE_INSTRUCTION(Xor); @@ -3179,7 +3637,7 @@ class HParameterValue : public HExpression<0> { class HNot : public HUnaryOperation { public: - explicit HNot(Primitive::Type result_type, HInstruction* input) + HNot(Primitive::Type result_type, HInstruction* input) : HUnaryOperation(result_type, input) {} bool CanBeMoved() const OVERRIDE { return true; } @@ -3188,8 +3646,14 @@ class HNot : public HUnaryOperation { return true; } - int32_t Evaluate(int32_t x) const OVERRIDE { return ~x; } - int64_t Evaluate(int64_t x) const OVERRIDE { return ~x; } + template <typename T> T Compute(T x) const { return ~x; } + + HConstant* Evaluate(HIntConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue())); + } + HConstant* Evaluate(HLongConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue())); + } DECLARE_INSTRUCTION(Not); @@ -3208,13 +3672,16 @@ class HBooleanNot : public HUnaryOperation { return true; } - int32_t Evaluate(int32_t x) const OVERRIDE { + template <typename T> bool Compute(T x) const { DCHECK(IsUint<1>(x)); return !x; } - int64_t Evaluate(int64_t x ATTRIBUTE_UNUSED) const OVERRIDE { - LOG(FATAL) << DebugName() << " cannot be used with 64-bit values"; + HConstant* Evaluate(HIntConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue())); + } + HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for long values"; UNREACHABLE(); } @@ -3228,7 +3695,8 @@ class HTypeConversion : public HExpression<1> { public: // Instantiate a type conversion of `input` to `result_type`. HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc) - : HExpression(result_type, SideEffects::None()), dex_pc_(dex_pc) { + : HExpression(result_type, SideEffectsForArchRuntimeCalls(input->GetType(), result_type)), + dex_pc_(dex_pc) { SetRawInputAt(0, input); DCHECK_NE(input->GetType(), result_type); } @@ -3248,6 +3716,18 @@ class HTypeConversion : public HExpression<1> { // containing the result. If the input cannot be converted, return nullptr. HConstant* TryStaticEvaluation() const; + static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type input_type, + Primitive::Type result_type) { + // Some architectures may not require the 'GC' side effects, but at this point + // in the compilation process we do not know what architecture we will + // generate code for, so we must be conservative. + if ((Primitive::IsFloatingPointType(input_type) && Primitive::IsIntegralType(result_type)) + || (input_type == Primitive::kPrimLong && Primitive::IsFloatingPointType(result_type))) { + return SideEffects::CanTriggerGC(); + } + return SideEffects::None(); + } + DECLARE_INSTRUCTION(TypeConversion); private: @@ -3283,6 +3763,8 @@ class HPhi : public HInstruction { } } + bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); } + size_t InputCount() const OVERRIDE { return inputs_.Size(); } void AddInput(HInstruction* input); @@ -3398,7 +3880,9 @@ class HInstanceFieldGet : public HExpression<1> { bool is_volatile, uint32_t field_idx, const DexFile& dex_file) - : HExpression(field_type, SideEffects::DependsOnSomething()), + : HExpression( + field_type, + SideEffects::FieldReadOfType(field_type, is_volatile)), field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) { SetRawInputAt(0, value); } @@ -3440,7 +3924,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { bool is_volatile, uint32_t field_idx, const DexFile& dex_file) - : HTemplateInstruction(SideEffects::ChangesSomething()), + : HTemplateInstruction( + SideEffects::FieldWriteOfType(field_type, is_volatile)), field_info_(field_offset, field_type, is_volatile, field_idx, dex_file), value_can_be_null_(true) { SetRawInputAt(0, object); @@ -3471,7 +3956,7 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { class HArrayGet : public HExpression<2> { public: HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type) - : HExpression(type, SideEffects::DependsOnSomething()) { + : HExpression(type, SideEffects::ArrayReadOfType(type)) { SetRawInputAt(0, array); SetRawInputAt(1, index); } @@ -3509,7 +3994,9 @@ class HArraySet : public HTemplateInstruction<3> { HInstruction* value, Primitive::Type expected_component_type, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::ChangesSomething()), + : HTemplateInstruction( + SideEffects::ArrayWriteOfType(expected_component_type).Union( + SideEffectsForArchRuntimeCalls(value->GetType()))), dex_pc_(dex_pc), expected_component_type_(expected_component_type), needs_type_check_(value->GetType() == Primitive::kPrimNot), @@ -3562,6 +4049,10 @@ class HArraySet : public HTemplateInstruction<3> { : expected_component_type_; } + static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { + return (value_type == Primitive::kPrimNot) ? SideEffects::CanTriggerGC() : SideEffects::None(); + } + DECLARE_INSTRUCTION(ArraySet); private: @@ -3656,7 +4147,7 @@ class HTemporary : public HTemplateInstruction<0> { class HSuspendCheck : public HTemplateInstruction<0> { public: explicit HSuspendCheck(uint32_t dex_pc) - : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc), slow_path_(nullptr) {} + : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc), slow_path_(nullptr) {} bool NeedsEnvironment() const OVERRIDE { return true; @@ -3688,13 +4179,13 @@ class HLoadClass : public HExpression<1> { const DexFile& dex_file, bool is_referrers_class, uint32_t dex_pc) - : HExpression(Primitive::kPrimNot, SideEffects::None()), + : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()), type_index_(type_index), dex_file_(dex_file), is_referrers_class_(is_referrers_class), dex_pc_(dex_pc), generate_clinit_check_(false), - loaded_class_rti_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) { + loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { SetRawInputAt(0, current_method); } @@ -3709,6 +4200,7 @@ class HLoadClass : public HExpression<1> { uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } uint16_t GetTypeIndex() const { return type_index_; } bool IsReferrersClass() const { return is_referrers_class_; } + bool CanBeNull() const OVERRIDE { return false; } bool NeedsEnvironment() const OVERRIDE { // Will call runtime and load the class if the class is not loaded yet. @@ -3744,14 +4236,14 @@ class HLoadClass : public HExpression<1> { loaded_class_rti_ = rti; } - bool IsResolved() { - return loaded_class_rti_.IsExact(); - } - const DexFile& GetDexFile() { return dex_file_; } bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; } + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(LoadClass); private: @@ -3771,7 +4263,7 @@ class HLoadClass : public HExpression<1> { class HLoadString : public HExpression<1> { public: HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc) - : HExpression(Primitive::kPrimNot, SideEffects::None()), + : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()), string_index_(string_index), dex_pc_(dex_pc) { SetRawInputAt(0, current_method); @@ -3792,6 +4284,10 @@ class HLoadString : public HExpression<1> { bool NeedsEnvironment() const OVERRIDE { return false; } bool NeedsDexCache() const OVERRIDE { return true; } + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(LoadString); private: @@ -3806,8 +4302,10 @@ class HLoadString : public HExpression<1> { */ class HClinitCheck : public HExpression<1> { public: - explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc) - : HExpression(Primitive::kPrimNot, SideEffects::ChangesSomething()), + HClinitCheck(HLoadClass* constant, uint32_t dex_pc) + : HExpression( + Primitive::kPrimNot, + SideEffects::AllChanges()), // Assume write/read on all fields/arrays. dex_pc_(dex_pc) { SetRawInputAt(0, constant); } @@ -3843,7 +4341,9 @@ class HStaticFieldGet : public HExpression<1> { bool is_volatile, uint32_t field_idx, const DexFile& dex_file) - : HExpression(field_type, SideEffects::DependsOnSomething()), + : HExpression( + field_type, + SideEffects::FieldReadOfType(field_type, is_volatile)), field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) { SetRawInputAt(0, cls); } @@ -3882,7 +4382,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> { bool is_volatile, uint32_t field_idx, const DexFile& dex_file) - : HTemplateInstruction(SideEffects::ChangesSomething()), + : HTemplateInstruction( + SideEffects::FieldWriteOfType(field_type, is_volatile)), field_info_(field_offset, field_type, is_volatile, field_idx, dex_file), value_can_be_null_(true) { SetRawInputAt(0, cls); @@ -3918,10 +4419,22 @@ class HLoadException : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HLoadException); }; +// Implicit part of move-exception which clears thread-local exception storage. +// Must not be removed because the runtime expects the TLS to get cleared. +class HClearException : public HTemplateInstruction<0> { + public: + HClearException() : HTemplateInstruction(SideEffects::AllWrites()) {} + + DECLARE_INSTRUCTION(ClearException); + + private: + DISALLOW_COPY_AND_ASSIGN(HClearException); +}; + class HThrow : public HTemplateInstruction<1> { public: HThrow(HInstruction* exception, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc) { + : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc) { SetRawInputAt(0, exception); } @@ -3947,7 +4460,7 @@ class HInstanceOf : public HExpression<2> { HLoadClass* constant, bool class_is_final, uint32_t dex_pc) - : HExpression(Primitive::kPrimBoolean, SideEffects::None()), + : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(class_is_final)), class_is_final_(class_is_final), must_do_null_check_(true), dex_pc_(dex_pc) { @@ -3973,6 +4486,10 @@ class HInstanceOf : public HExpression<2> { bool MustDoNullCheck() const { return must_do_null_check_; } void ClearMustDoNullCheck() { must_do_null_check_ = false; } + static SideEffects SideEffectsForArchRuntimeCalls(bool class_is_final) { + return class_is_final ? SideEffects::None() : SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(InstanceOf); private: @@ -3985,27 +4502,43 @@ class HInstanceOf : public HExpression<2> { class HBoundType : public HExpression<1> { public: - HBoundType(HInstruction* input, ReferenceTypeInfo bound_type) + // Constructs an HBoundType with the given upper_bound. + // Ensures that the upper_bound is valid. + HBoundType(HInstruction* input, ReferenceTypeInfo upper_bound, bool upper_can_be_null) : HExpression(Primitive::kPrimNot, SideEffects::None()), - bound_type_(bound_type) { + upper_bound_(upper_bound), + upper_can_be_null_(upper_can_be_null), + can_be_null_(upper_can_be_null) { DCHECK_EQ(input->GetType(), Primitive::kPrimNot); SetRawInputAt(0, input); + SetReferenceTypeInfo(upper_bound_); } - const ReferenceTypeInfo& GetBoundType() const { return bound_type_; } + // GetUpper* should only be used in reference type propagation. + const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } + bool GetUpperCanBeNull() const { return upper_can_be_null_; } - bool CanBeNull() const OVERRIDE { - // `null instanceof ClassX` always return false so we can't be null. - return false; + void SetCanBeNull(bool can_be_null) { + DCHECK(upper_can_be_null_ || !can_be_null); + can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return can_be_null_; } + DECLARE_INSTRUCTION(BoundType); private: // Encodes the most upper class that this instruction can have. In other words - // it is always the case that GetBoundType().IsSupertypeOf(GetReferenceType()). - // It is used to bound the type in cases like `if (x instanceof ClassX) {}` - const ReferenceTypeInfo bound_type_; + // it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()). + // It is used to bound the type in cases like: + // if (x instanceof ClassX) { + // // uper_bound_ will be ClassX + // } + const ReferenceTypeInfo upper_bound_; + // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this + // is false then can_be_null_ cannot be true). + const bool upper_can_be_null_; + bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HBoundType); }; @@ -4016,7 +4549,7 @@ class HCheckCast : public HTemplateInstruction<2> { HLoadClass* constant, bool class_is_final, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::None()), + : HTemplateInstruction(SideEffects::CanTriggerGC()), class_is_final_(class_is_final), must_do_null_check_(true), dex_pc_(dex_pc) { @@ -4057,7 +4590,8 @@ class HCheckCast : public HTemplateInstruction<2> { class HMemoryBarrier : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind) - : HTemplateInstruction(SideEffects::None()), + : HTemplateInstruction( + SideEffects::AllWritesAndReads()), // Assume write/read on all fields/arrays. barrier_kind_(barrier_kind) {} MemBarrierKind GetBarrierKind() { return barrier_kind_; } @@ -4078,13 +4612,21 @@ class HMonitorOperation : public HTemplateInstruction<1> { }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::None()), kind_(kind), dex_pc_(dex_pc) { + : HTemplateInstruction( + SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays. + kind_(kind), dex_pc_(dex_pc) { SetRawInputAt(0, object); } // Instruction may throw a Java exception, so we need an environment. - bool NeedsEnvironment() const OVERRIDE { return true; } - bool CanThrow() const OVERRIDE { return true; } + bool NeedsEnvironment() const OVERRIDE { return CanThrow(); } + + bool CanThrow() const OVERRIDE { + // Verifier guarantees that monitor-exit cannot throw. + // This is important because it allows the HGraphBuilder to remove + // a dead throw-catch loop generated for `synchronized` blocks/methods. + return IsEnter(); + } uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } @@ -4100,6 +4642,25 @@ class HMonitorOperation : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); }; +/** + * A HInstruction used as a marker for the replacement of new + <init> + * of a String to a call to a StringFactory. Only baseline will see + * the node at code generation, where it will be be treated as null. + * When compiling non-baseline, `HFakeString` instructions are being removed + * in the instruction simplifier. + */ +class HFakeString : public HTemplateInstruction<0> { + public: + HFakeString() : HTemplateInstruction(SideEffects::None()) {} + + Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimNot; } + + DECLARE_INSTRUCTION(FakeString); + + private: + DISALLOW_COPY_AND_ASSIGN(HFakeString); +}; + class MoveOperands : public ArenaObject<kArenaAllocMisc> { public: MoveOperands(Location source, |