OSDN Git Service

Add a GVN dependency 'GC' for garbage collection.
authorAlexandre Rames <alexandre.rames@linaro.org>
Wed, 12 Aug 2015 12:43:29 +0000 (13:43 +0100)
committerAlexandre Rames <alexandre.rames@linaro.org>
Wed, 12 Aug 2015 12:43:29 +0000 (13:43 +0100)
This will be used by incoming architecture specific optimizations. The
dependencies must be conservative. When an HInstruction is created we
may not be sure whether it can trigger GC. In that case the
'ChangesGC' dependency must be set. We control at code-generation time
that HInstructions that can call have the 'ChangesGC' dependency
set.

Change-Id: Iea6a7f430009f37a9599b0a0039207049906e45d

13 files changed:
compiler/optimizing/code_generator.cc
compiler/optimizing/code_generator.h
compiler/optimizing/code_generator_arm.cc
compiler/optimizing/code_generator_arm64.cc
compiler/optimizing/code_generator_mips64.cc
compiler/optimizing/code_generator_x86.cc
compiler/optimizing/code_generator_x86_64.cc
compiler/optimizing/dead_code_elimination.cc
compiler/optimizing/gvn.cc
compiler/optimizing/gvn_test.cc
compiler/optimizing/nodes.h
compiler/optimizing/side_effects_analysis.cc
compiler/optimizing/side_effects_test.cc

index 4607ebe..77d6628 100644 (file)
@@ -1005,6 +1005,31 @@ void CodeGenerator::EmitParallelMoves(Location from1,
   GetMoveResolver()->EmitNativeCode(&parallel_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);
 }
index 540da1c..2582444 100644 (file)
@@ -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);
   }
index 6c0292c..1bd4216 100644 (file)
@@ -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) {
index b44c5ba..e5b6df5 100644 (file)
@@ -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,
index b6ebeb4..e5761eb 100644 (file)
@@ -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,
index 4efdbb9..5a77c82 100644 (file)
@@ -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,
index 1585104..3730478 100644 (file)
@@ -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;
index 6269d16..5de629d 100644 (file)
@@ -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.
index 3900646..833dfb0 100644 (file)
@@ -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;
index 5c6239b..42ef3ff 100644 (file)
@@ -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.
index ca2c998..dba9233 100644 (file)
@@ -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);
   }
index 9dbf638..1c3e255 100644 (file)
@@ -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;
       }
index 8db5a8a..ec45d6b 100644 (file)
@@ -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());
 }