diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/optimizing/code_generator.cc | 25 | ||||
-rw-r--r-- | compiler/optimizing/code_generator.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 15 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 14 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 14 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 14 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 14 | ||||
-rw-r--r-- | compiler/optimizing/dead_code_elimination.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/gvn.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/gvn_test.cc | 7 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 260 | ||||
-rw-r--r-- | compiler/optimizing/side_effects_analysis.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/side_effects_test.cc | 44 |
13 files changed, 276 insertions, 141 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 4607ebe548..77d6628ff6 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1005,6 +1005,31 @@ void CodeGenerator::EmitParallelMoves(Location from1, GetMoveResolver()->EmitNativeCode(¶llel_move); } +void CodeGenerator::ValidateInvokeRuntime(HInstruction* instruction, SlowPathCode* slow_path) { + // Ensure that the call kind indication given to the register allocator is + // coherent with the runtime call generated, and that the GC side effect is + // set when required. + if (slow_path == nullptr) { + DCHECK(instruction->GetLocations()->WillCall()); + DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())); + } else { + DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal()); + DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) || + // Control flow would not come back into the code if a fatal slow + // path is taken, so we do not care if it triggers GC. + slow_path->IsFatal() || + // HDeoptimize is a special case: we know we are not coming back from + // it into the code. + instruction->IsDeoptimize()); + } + + // Check the coherency of leaf information. + DCHECK(instruction->IsSuspendCheck() + || ((slow_path != nullptr) && slow_path->IsFatal()) + || instruction->GetLocations()->CanCall() + || !IsLeafMethod()); +} + void SlowPathCode::RecordPcInfo(CodeGenerator* codegen, HInstruction* instruction, uint32_t dex_pc) { codegen->RecordPcInfo(instruction, dex_pc, this); } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 540da1c866..25824448c5 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -292,6 +292,8 @@ class CodeGenerator { return type == Primitive::kPrimNot && !value->IsNullConstant(); } + void ValidateInvokeRuntime(HInstruction* instruction, SlowPathCode* slow_path); + void AddAllocatedRegister(Location location) { allocated_registers_.Add(location); } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 6c0292c551..1bd42160d7 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -953,23 +953,10 @@ void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path) { - // Ensure that the call kind indication given to the register allocator is - // coherent with the runtime call generated. - if (slow_path == nullptr) { - DCHECK(instruction->GetLocations()->WillCall()); - } else { - DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal()); - } - + ValidateInvokeRuntime(instruction, slow_path); __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); __ blx(LR); RecordPcInfo(instruction, dex_pc, slow_path); - DCHECK(instruction->IsSuspendCheck() - || instruction->IsBoundsCheck() - || instruction->IsNullCheck() - || instruction->IsDivZeroCheck() - || instruction->GetLocations()->CanCall() - || !IsLeafMethod()); } void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b44c5ba9f8..e5b6df54d9 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1103,23 +1103,11 @@ void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path) { - // Ensure that the call kind indication given to the register allocator is - // coherent with the runtime call generated. - if (slow_path == nullptr) { - DCHECK(instruction->GetLocations()->WillCall()); - } else { - DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal()); - } - + ValidateInvokeRuntime(instruction, slow_path); BlockPoolsScope block_pools(GetVIXLAssembler()); __ Ldr(lr, MemOperand(tr, entry_point_offset)); __ Blr(lr); RecordPcInfo(instruction, dex_pc, slow_path); - DCHECK(instruction->IsSuspendCheck() - || instruction->IsBoundsCheck() - || instruction->IsNullCheck() - || instruction->IsDivZeroCheck() - || !IsLeafMethod()); } void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index b6ebeb4977..e5761eb9f1 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -977,23 +977,11 @@ void CodeGeneratorMIPS64::InvokeRuntime(int32_t entry_point_offset, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path) { - // Ensure that the call kind indication given to the register allocator is - // coherent with the runtime call generated. - if (slow_path == nullptr) { - DCHECK(instruction->GetLocations()->WillCall()); - } else { - DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal()); - } - + ValidateInvokeRuntime(instruction, slow_path); // TODO: anything related to T9/GP/GOT/PIC/.so's? __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset); __ Jalr(T9); RecordPcInfo(instruction, dex_pc, slow_path); - DCHECK(instruction->IsSuspendCheck() - || instruction->IsBoundsCheck() - || instruction->IsNullCheck() - || instruction->IsDivZeroCheck() - || !IsLeafMethod()); } void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4efdbb922e..5a77c82aa4 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -429,21 +429,9 @@ void CodeGeneratorX86::InvokeRuntime(Address entry_point, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path) { - // Ensure that the call kind indication given to the register allocator is - // coherent with the runtime call generated. - if (slow_path == nullptr) { - DCHECK(instruction->GetLocations()->WillCall()); - } else { - DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal()); - } - + ValidateInvokeRuntime(instruction, slow_path); __ fs()->call(entry_point); RecordPcInfo(instruction, dex_pc, slow_path); - DCHECK(instruction->IsSuspendCheck() - || instruction->IsBoundsCheck() - || instruction->IsNullCheck() - || instruction->IsDivZeroCheck() - || !IsLeafMethod()); } CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 1585104789..37304781e0 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -484,21 +484,9 @@ void CodeGeneratorX86_64::InvokeRuntime(Address entry_point, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path) { - // Ensure that the call kind indication given to the register allocator is - // coherent with the runtime call generated. - if (slow_path == nullptr) { - DCHECK(instruction->GetLocations()->WillCall()); - } else { - DCHECK(instruction->GetLocations()->OnlyCallsOnSlowPath() || slow_path->IsFatal()); - } - + ValidateInvokeRuntime(instruction, slow_path); __ gs()->call(entry_point); RecordPcInfo(instruction, dex_pc, slow_path); - DCHECK(instruction->IsSuspendCheck() - || instruction->IsBoundsCheck() - || instruction->IsNullCheck() - || instruction->IsDivZeroCheck() - || !IsLeafMethod()); } static constexpr int kNumberOfCpuRegisterPairs = 0; diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 6269d1628e..5de629d605 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -128,7 +128,7 @@ void HDeadCodeElimination::RemoveDeadInstructions() { for (i.Advance(); !i.Done(); i.Advance()) { HInstruction* inst = i.Current(); DCHECK(!inst->IsControlFlow()); - if (!inst->DoesAnyWrite() + if (!inst->HasSideEffects() && !inst->CanThrow() && !inst->IsSuspendCheck() // If we added an explicit barrier then we should keep it. diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index 39006465d5..833dfb00a1 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -264,7 +264,7 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> { // odd buckets to speed up deletion. size_t HashCode(HInstruction* instruction) const { size_t hash_code = instruction->ComputeHashCode(); - if (instruction->GetSideEffects().DoesAnyRead()) { + if (instruction->GetSideEffects().HasDependencies()) { return (hash_code << 1) | 0; } else { return (hash_code << 1) | 1; diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index 5c6239b3f9..42ef3ff4a5 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -266,6 +266,8 @@ TEST(GVNTest, LoopSideEffects) { ArenaPool pool; ArenaAllocator allocator(&pool); + static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC(); + HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry = new (&allocator) HBasicBlock(graph); graph->AddBlock(entry); @@ -309,7 +311,7 @@ TEST(GVNTest, LoopSideEffects) { ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn( *outer_loop_header->GetLoopInformation())); - // Check that the loops don't have side effects. + // Check that the only side effect of loops is to potentially trigger GC. { // Make one block with a side effect. entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter, @@ -327,6 +329,8 @@ TEST(GVNTest, LoopSideEffects) { ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite()); ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite()); ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite()); + ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).Equals(kCanTriggerGC)); + ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC)); } // Check that the side effects of the outer loop does not affect the inner loop. @@ -348,6 +352,7 @@ TEST(GVNTest, LoopSideEffects) { ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite()); ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite()); ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite()); + ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC)); } // Check that the side effects of the inner loop affects the outer loop. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ca2c9989b0..dba9233240 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1215,23 +1215,33 @@ class HUserRecord : public ValueObject { }; /** - * Side-effects representation for write/read dependences on fields/arrays. + * Side-effects representation. * - * 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). + * 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 the following 36-bit flags assignments: + * 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). * - * |ARRAY-R |FIELD-R |ARRAY-W |FIELD-W | - * +---------+---------+---------+---------+ - * |543210987|654321098|765432109|876543210| - * |DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL| + * |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: @@ -1242,6 +1252,22 @@ class SideEffects : public ValueObject { } static SideEffects All() { + 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); } @@ -1255,7 +1281,7 @@ class SideEffects : public ValueObject { static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) { return is_volatile - ? All() + ? AllWritesAndReads() : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset)); } @@ -1265,7 +1291,7 @@ class SideEffects : public ValueObject { static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) { return is_volatile - ? All() + ? AllWritesAndReads() : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset)); } @@ -1273,11 +1299,40 @@ class SideEffects : public ValueObject { 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_); } + SideEffects Exclusion(SideEffects other) const { + return SideEffects(flags_ & ~other.flags_); + } + + bool Includes(SideEffects other) const { + return (other.flags_ & flags_) == other.flags_; + } + + bool HasSideEffects() const { + return (flags_ & kAllChangeBits); + } + + bool HasDependencies() const { + 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); @@ -1288,47 +1343,81 @@ class SideEffects : public ValueObject { return (flags_ & kAllReads); } - // Returns true if nothing is written or read. - bool DoesNothing() const { - return flags_ == 0; - } - // 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_ == (kAllWrites | kAllReads); + return flags_ == (kAllChangeBits | kAllDependOnBits); } // Returns true if this may read something written by other. bool MayDependOn(SideEffects other) const { - const uint64_t reads = (flags_ & kAllReads) >> kFieldReadOffset; - return (other.flags_ & reads); + const uint64_t depends_on_flags = (flags_ & kAllDependOnBits) >> kChangeBits; + return (other.flags_ & depends_on_flags); } // Returns string representation of flags (for debugging only). - // Format: |DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL| + // Format: |x|DFJISCBZL|DFJISCBZL|y|DFJISCBZL|DFJISCBZL| std::string ToString() const { - static const char *kDebug = "LZBCSIJFD"; std::string flags = "|"; - for (int s = 35; s >= 0; s--) { - const int t = s % kBits; - if ((flags_ >> s) & 1) - flags += kDebug[t]; - if (t == 0) + 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 kBits = 9; - static constexpr int kFieldWriteOffset = 0 * kBits; - static constexpr int kArrayWriteOffset = 1 * kBits; - static constexpr int kFieldReadOffset = 2 * kBits; - static constexpr int kArrayReadOffset = 3 * kBits; + 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 uint64_t kAllWrites = 0x0003ffff; - static constexpr uint64_t kAllReads = kAllWrites << kFieldReadOffset; + // Aliases. + + static_assert(kChangeBits == kDependOnBits, + "the 'change' bits should match the 'depend on' bits."); + + 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 @@ -1610,6 +1699,7 @@ 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 @@ -2302,7 +2392,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); } @@ -2626,7 +2718,9 @@ class HCompare : public HBinaryOperation { HInstruction* second, 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()); } @@ -2649,7 +2743,12 @@ class HCompare : public HBinaryOperation { bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; } - uint32_t GetDexPc() const { return dex_pc_; } + 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); @@ -2851,7 +2950,8 @@ class HInvoke : public HInstruction { uint32_t dex_pc, uint32_t dex_method_index, InvokeType original_invoke_type) - : HInstruction(SideEffects::All()), // assume write/read on all fields/arrays + : 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), @@ -3068,7 +3168,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), @@ -3131,7 +3231,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), @@ -3232,7 +3332,8 @@ 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) {} template <typename T> T Compute(T x, T y) const { @@ -3252,6 +3353,11 @@ class HDiv : public HBinaryOperation { 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: @@ -3263,7 +3369,8 @@ 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) {} template <typename T> T Compute(T x, T y) const { @@ -3283,6 +3390,10 @@ class HRem : public HBinaryOperation { uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(Rem); private: @@ -3593,7 +3704,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); } @@ -3613,6 +3725,19 @@ 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 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) + && result_type == Primitive::kPrimLong) + || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat)) { + return SideEffects::CanTriggerGC(); + } + return SideEffects::None(); + } + DECLARE_INSTRUCTION(TypeConversion); private: @@ -3879,7 +4004,9 @@ class HArraySet : public HTemplateInstruction<3> { HInstruction* value, Primitive::Type expected_component_type, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::ArrayWriteOfType(expected_component_type)), + : 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), @@ -3932,6 +4059,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: @@ -4026,7 +4157,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; @@ -4058,7 +4189,7 @@ 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), @@ -4119,6 +4250,10 @@ class HLoadClass : public HExpression<1> { bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; } + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + DECLARE_INSTRUCTION(LoadClass); private: @@ -4138,7 +4273,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); @@ -4159,6 +4294,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: @@ -4175,8 +4314,8 @@ class HClinitCheck : public HExpression<1> { public: explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc) : HExpression( - Primitive::kPrimNot, - SideEffects::AllWrites()), // assume write on all fields/arrays + Primitive::kPrimNot, + SideEffects::AllChanges()), // Assume write/read on all fields/arrays. dex_pc_(dex_pc) { SetRawInputAt(0, constant); } @@ -4305,7 +4444,7 @@ class HClearException : public HTemplateInstruction<0> { 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); } @@ -4331,7 +4470,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) { @@ -4357,6 +4496,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: @@ -4416,7 +4559,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) { @@ -4458,7 +4601,7 @@ class HMemoryBarrier : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind) : HTemplateInstruction( - SideEffects::All()), // assume write/read on all fields/arrays + SideEffects::AllWritesAndReads()), // Assume write/read on all fields/arrays. barrier_kind_(barrier_kind) {} MemBarrierKind GetBarrierKind() { return barrier_kind_; } @@ -4479,7 +4622,8 @@ class HMonitorOperation : public HTemplateInstruction<1> { }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::All()), // assume write/read on all fields/arrays + : HTemplateInstruction( + SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays. kind_(kind), dex_pc_(dex_pc) { SetRawInputAt(0, object); } diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc index 9dbf638442..1c3e255339 100644 --- a/compiler/optimizing/side_effects_analysis.cc +++ b/compiler/optimizing/side_effects_analysis.cc @@ -47,8 +47,8 @@ void SideEffectsAnalysis::Run() { inst_it.Advance()) { HInstruction* instruction = inst_it.Current(); effects = effects.Union(instruction->GetSideEffects()); - // If every possible write/read is represented, scanning further - // will not add any more information to side-effects of this block. + // If all side effects are represented, scanning further will not add any + // more information to side-effects of this block. if (effects.DoesAll()) { break; } diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc index 8db5a8a350..ec45d6b2ca 100644 --- a/compiler/optimizing/side_effects_test.cc +++ b/compiler/optimizing/side_effects_test.cc @@ -77,7 +77,7 @@ TEST(SideEffectsTest, All) { EXPECT_TRUE(all.DoesAnyWrite()); EXPECT_TRUE(all.DoesAnyRead()); EXPECT_FALSE(all.DoesNothing()); - EXPECT_TRUE(all.DoesAll()); + EXPECT_TRUE(all.DoesAllReadWrite()); } TEST(SideEffectsTest, None) { @@ -85,7 +85,7 @@ TEST(SideEffectsTest, None) { EXPECT_FALSE(none.DoesAnyWrite()); EXPECT_FALSE(none.DoesAnyRead()); EXPECT_TRUE(none.DoesNothing()); - EXPECT_FALSE(none.DoesAll()); + EXPECT_FALSE(none.DoesAllReadWrite()); } TEST(SideEffectsTest, DependencesAndNoDependences) { @@ -176,33 +176,53 @@ TEST(SideEffectsTest, AllWritesAndReads) { s = s.Union(SideEffects::FieldReadOfType(type, false)); s = s.Union(SideEffects::ArrayReadOfType(type)); } - EXPECT_TRUE(s.DoesAll()); + EXPECT_TRUE(s.DoesAllReadWrite()); +} + +TEST(SideEffectsTest, GC) { + SideEffects can_trigger_gc = SideEffects::CanTriggerGC(); + SideEffects depends_on_gc = SideEffects::DependsOnGC(); + SideEffects all_changes = SideEffects::AllChanges(); + SideEffects all_dependencies = SideEffects::AllDependencies(); + + EXPECT_TRUE(depends_on_gc.MayDependOn(can_trigger_gc)); + EXPECT_TRUE(depends_on_gc.Union(can_trigger_gc).MayDependOn(can_trigger_gc)); + EXPECT_FALSE(can_trigger_gc.MayDependOn(depends_on_gc)); + + EXPECT_TRUE(depends_on_gc.MayDependOn(all_changes)); + EXPECT_TRUE(depends_on_gc.Union(can_trigger_gc).MayDependOn(all_changes)); + EXPECT_FALSE(can_trigger_gc.MayDependOn(all_changes)); + + EXPECT_TRUE(all_changes.Includes(can_trigger_gc)); + EXPECT_FALSE(all_changes.Includes(depends_on_gc)); + EXPECT_TRUE(all_dependencies.Includes(depends_on_gc)); + EXPECT_FALSE(all_dependencies.Includes(can_trigger_gc)); } TEST(SideEffectsTest, BitStrings) { EXPECT_STREQ( - "|||||", + "|||||||", SideEffects::None().ToString().c_str()); EXPECT_STREQ( - "|DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL|", + "|GC|DFJISCBZL|DFJISCBZL|GC|DFJISCBZL|DFJISCBZL|", SideEffects::All().ToString().c_str()); EXPECT_STREQ( - "|||DFJISCBZL|DFJISCBZL|", + "|||||DFJISCBZL|DFJISCBZL|", SideEffects::AllWrites().ToString().c_str()); EXPECT_STREQ( - "|DFJISCBZL|DFJISCBZL|||", + "||DFJISCBZL|DFJISCBZL||||", SideEffects::AllReads().ToString().c_str()); EXPECT_STREQ( - "||||L|", + "||||||L|", SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str()); EXPECT_STREQ( - "|||Z||", + "|||||Z||", SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str()); EXPECT_STREQ( - "||B|||", + "|||B||||", SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str()); EXPECT_STREQ( - "|DJ||||", // note: DJ alias + "||DJ|||||", // note: DJ alias SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str()); SideEffects s = SideEffects::None(); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, false)); @@ -212,7 +232,7 @@ TEST(SideEffectsTest, BitStrings) { s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); EXPECT_STREQ( - "|DFJI|FI|S|DJC|", // note: DJ/FI alias. + "||DFJI|FI||S|DJC|", // note: DJ/FI alias. s.ToString().c_str()); } |