Improved side effect analysis (field/array write/read).

Rationale:
Types (int, float etc.) and access type (field vs. array)
can be used to disambiguate write/read side-effects analysis.
This directly improves e.g. dead code elimination and licm.

Change-Id: I371f6909a3f42bda13190a03f04c4a867bde1d06
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 5de629d..6269d16 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -128,7 +128,7 @@
     for (i.Advance(); !i.Done(); i.Advance()) {
       HInstruction* inst = i.Current();
       DCHECK(!inst->IsControlFlow());
-      if (!inst->HasSideEffects()
+      if (!inst->DoesAnyWrite()
           && !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 708733e..3900646 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -120,7 +120,7 @@
   // Removes all instructions in the set affected by the given side effects.
   void Kill(SideEffects side_effects) {
     DeleteAllImpureWhich([side_effects](Node* node) {
-      return node->GetInstruction()->GetSideEffects().DependsOn(side_effects);
+      return node->GetInstruction()->GetSideEffects().MayDependOn(side_effects);
     });
   }
 
@@ -264,7 +264,7 @@
   // odd buckets to speed up deletion.
   size_t HashCode(HInstruction* instruction) const {
     size_t hash_code = instruction->ComputeHashCode();
-    if (instruction->GetSideEffects().HasDependencies()) {
+    if (instruction->GetSideEffects().DoesAnyRead()) {
       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 d8a09ff..5c6239b 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -206,7 +206,7 @@
   // and the body to be GVN'ed.
   loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
                                                                parameter,
-                                                               Primitive::kPrimNot,
+                                                               Primitive::kPrimBoolean,
                                                                MemberOffset(42),
                                                                false,
                                                                kUnknownFieldIndex,
@@ -323,9 +323,10 @@
     SideEffectsAnalysis side_effects(graph);
     side_effects.Run();
 
-    ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
-    ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
-    ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite());
+    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());
   }
 
   // Check that the side effects of the outer loop does not affect the inner loop.
@@ -343,10 +344,10 @@
     SideEffectsAnalysis side_effects(graph);
     side_effects.Run();
 
-    ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
-    ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
-    ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
-    ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite());
+    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());
   }
 
   // Check that the side effects of the inner loop affects the outer loop.
@@ -365,10 +366,10 @@
     SideEffectsAnalysis side_effects(graph);
     side_effects.Run();
 
-    ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
-    ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
-    ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
-    ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite());
+    ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
+    ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
+    ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
   }
 }
 }  // namespace art
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index 2535ea2..5b89b4e 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -115,7 +115,7 @@
         HInstruction* instruction = inst_it.Current();
         if (instruction->CanBeMoved()
             && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop)
-            && !instruction->GetSideEffects().DependsOn(loop_effects)
+            && !instruction->GetSideEffects().MayDependOn(loop_effects)
             && InputsAreDefinedBeforeLoop(instruction)) {
           // We need to update the environment if the instruction has a loop header
           // phi in it.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8546a10..32459a9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1155,13 +1155,25 @@
   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 the following 36-bit flags assignments:
+ *
+ *   |ARRAY-R  |FIELD-R  |ARRAY-W  |FIELD-W  |
+ *   +---------+---------+---------+---------+
+ *   |543210987|654321098|765432109|876543210|
+ *   |DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL|
+ */
 class SideEffects : public ValueObject {
  public:
   SideEffects() : flags_(0) {}
@@ -1171,57 +1183,113 @@
   }
 
   static SideEffects All() {
-    return SideEffects(ChangesSomething().flags_ | DependsOnSomething().flags_);
+    return SideEffects(kAllWrites | kAllReads);
   }
 
-  static SideEffects ChangesSomething() {
-    return SideEffects((1 << kFlagChangesCount) - 1);
+  static SideEffects FieldWriteOfType(Primitive::Type type) {
+    return 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) {
+    return SideEffects(TypeFlagWithAlias(type, kFieldReadOffset));
+  }
+
+  static SideEffects ArrayReadOfType(Primitive::Type type) {
+    return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset));
+  }
+
+  // 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;
+  // Returns true if something is written.
+  bool DoesAnyWrite() const {
+    return (flags_ & kAllWrites);
   }
 
-  bool HasAllSideEffects() const {
-    size_t all_bits_set = (1 << kFlagChangesCount) - 1;
-    return all_bits_set == (flags_ & all_bits_set);
+  // Returns true if something is read.
+  bool DoesAnyRead() const {
+    return (flags_ & kAllReads);
   }
 
-  bool DependsOn(SideEffects other) const {
-    size_t depends_flags = other.ComputeDependsFlags();
-    return (flags_ & depends_flags) != 0;
+  // Returns true if nothing is written or read.
+  bool DoesNothing() const {
+    return flags_ == 0;
   }
 
-  bool HasDependencies() const {
-    int count = kFlagDependsOnCount - kFlagChangesCount;
-    size_t all_bits_set = (1 << count) - 1;
-    return ((flags_ >> kFlagChangesCount) & all_bits_set) != 0;
+  // Returns true if potentially everything is written and read
+  // (every type and every kind of access).
+  bool DoesAll() const {
+    return flags_ == (kAllWrites | kAllReads);
+  }
+
+  // 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);
+  }
+
+  // Returns string representation of flags (for debugging only).
+  // Format: |DFJISCBZL|DFJISCBZL|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)
+        flags += "|";
+    }
+    return flags;
   }
 
  private:
-  static constexpr int kFlagChangesSomething = 0;
-  static constexpr int kFlagChangesCount = kFlagChangesSomething + 1;
+  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 kFlagDependsOnSomething = kFlagChangesCount;
-  static constexpr int kFlagDependsOnCount = kFlagDependsOnSomething + 1;
+  static constexpr uint64_t kAllWrites = 0x0003ffff;
+  static constexpr uint64_t kAllReads  = kAllWrites << kFieldReadOffset;
 
-  explicit SideEffects(size_t flags) : flags_(flags) {}
-
-  size_t ComputeDependsFlags() const {
-    return flags_ << kFlagChangesCount;
+  // 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.
@@ -1484,7 +1552,8 @@
   }
   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.
@@ -2692,7 +2761,7 @@
           uint32_t dex_pc,
           uint32_t dex_method_index,
           InvokeType original_invoke_type)
-    : HInstruction(SideEffects::All()),
+    : HInstruction(SideEffects::All()),  // assume write/read on all fields/arrays
       number_of_arguments_(number_of_arguments),
       inputs_(arena, number_of_arguments),
       return_type_(return_type),
@@ -3482,7 +3551,7 @@
                     bool is_volatile,
                     uint32_t field_idx,
                     const DexFile& dex_file)
-      : HExpression(field_type, SideEffects::DependsOnSomething()),
+      : HExpression(field_type, SideEffects::SideEffects::FieldReadOfType(field_type)),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
     SetRawInputAt(0, value);
   }
@@ -3524,7 +3593,7 @@
                     bool is_volatile,
                     uint32_t field_idx,
                     const DexFile& dex_file)
-      : HTemplateInstruction(SideEffects::ChangesSomething()),
+      : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type)),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file),
         value_can_be_null_(true) {
     SetRawInputAt(0, object);
@@ -3555,7 +3624,7 @@
 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);
   }
@@ -3593,7 +3662,7 @@
             HInstruction* value,
             Primitive::Type expected_component_type,
             uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::ChangesSomething()),
+      : HTemplateInstruction(SideEffects::ArrayWriteOfType(expected_component_type)),
         dex_pc_(dex_pc),
         expected_component_type_(expected_component_type),
         needs_type_check_(value->GetType() == Primitive::kPrimNot),
@@ -3892,7 +3961,9 @@
 class HClinitCheck : public HExpression<1> {
  public:
   explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
-      : HExpression(Primitive::kPrimNot, SideEffects::ChangesSomething()),
+      : HExpression(
+          Primitive::kPrimNot,
+          SideEffects::All()),  // assume write/read on all fields/arrays
         dex_pc_(dex_pc) {
     SetRawInputAt(0, constant);
   }
@@ -3928,7 +3999,7 @@
                   bool is_volatile,
                   uint32_t field_idx,
                   const DexFile& dex_file)
-      : HExpression(field_type, SideEffects::DependsOnSomething()),
+      : HExpression(field_type, SideEffects::SideEffects::FieldReadOfType(field_type)),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
     SetRawInputAt(0, cls);
   }
@@ -3967,7 +4038,7 @@
                   bool is_volatile,
                   uint32_t field_idx,
                   const DexFile& dex_file)
-      : HTemplateInstruction(SideEffects::ChangesSomething()),
+      : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type)),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file),
         value_can_be_null_(true) {
     SetRawInputAt(0, cls);
@@ -4163,7 +4234,8 @@
   };
 
   HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
-    : HTemplateInstruction(SideEffects::ChangesSomething()), kind_(kind), dex_pc_(dex_pc) {
+    : HTemplateInstruction(SideEffects::All()),  // assume write/read on all fields/arrays
+      kind_(kind), dex_pc_(dex_pc) {
     SetRawInputAt(0, object);
   }
 
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index bc56546..f793a65 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -40,7 +40,7 @@
   // Return the name of the pass.
   const char* GetPassName() const { return pass_name_; }
 
-  // Peform the analysis itself.
+  // Perform the analysis itself.
   virtual void Run() = 0;
 
  protected:
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index ea1ca5a..9dbf638 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -24,14 +24,15 @@
   block_effects_.SetSize(graph_->GetBlocks().Size());
   loop_effects_.SetSize(graph_->GetBlocks().Size());
 
+  // In DEBUG mode, ensure side effects are properly initialized to empty.
   if (kIsDebugBuild) {
     for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
       HBasicBlock* block = it.Current();
       SideEffects effects = GetBlockEffects(block);
-      DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+      DCHECK(effects.DoesNothing());
       if (block->IsLoopHeader()) {
         effects = GetLoopEffects(block);
-        DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+        DCHECK(effects.DoesNothing());
       }
     }
   }
@@ -46,7 +47,9 @@
          inst_it.Advance()) {
       HInstruction* instruction = inst_it.Current();
       effects = effects.Union(instruction->GetSideEffects());
-      if (effects.HasAllSideEffects()) {
+      // If every possible write/read is 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
new file mode 100644
index 0000000..813f7ce
--- /dev/null
+++ b/compiler/optimizing/side_effects_test.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not read this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+#include "nodes.h"
+#include "primitive.h"
+
+namespace art {
+
+/**
+ * Tests for the SideEffects class.
+ */
+
+//
+// Helper methods.
+//
+
+void testWriteAndReadSanity(SideEffects write, SideEffects read) {
+  EXPECT_FALSE(write.DoesNothing());
+  EXPECT_FALSE(read.DoesNothing());
+
+  EXPECT_TRUE(write.DoesAnyWrite());
+  EXPECT_FALSE(write.DoesAnyRead());
+  EXPECT_FALSE(read.DoesAnyWrite());
+  EXPECT_TRUE(read.DoesAnyRead());
+
+  // All-dependences.
+  SideEffects all = SideEffects::All();
+  EXPECT_TRUE(all.MayDependOn(write));
+  EXPECT_FALSE(write.MayDependOn(all));
+  EXPECT_FALSE(all.MayDependOn(read));
+  EXPECT_TRUE(read.MayDependOn(all));
+
+  // None-dependences.
+  SideEffects none = SideEffects::None();
+  EXPECT_FALSE(none.MayDependOn(write));
+  EXPECT_FALSE(write.MayDependOn(none));
+  EXPECT_FALSE(none.MayDependOn(read));
+  EXPECT_FALSE(read.MayDependOn(none));
+}
+
+void testWriteAndReadDependence(SideEffects write, SideEffects read) {
+  testWriteAndReadSanity(write, read);
+
+  // Dependence only in one direction.
+  EXPECT_FALSE(write.MayDependOn(read));
+  EXPECT_TRUE(read.MayDependOn(write));
+}
+
+void testNoWriteAndReadDependence(SideEffects write, SideEffects read) {
+  testWriteAndReadSanity(write, read);
+
+  // No dependence in any direction.
+  EXPECT_FALSE(write.MayDependOn(read));
+  EXPECT_FALSE(read.MayDependOn(write));
+}
+
+//
+// Actual tests.
+//
+
+TEST(SideEffectsTest, All) {
+  SideEffects all = SideEffects::All();
+  EXPECT_TRUE(all.DoesAnyWrite());
+  EXPECT_TRUE(all.DoesAnyRead());
+  EXPECT_FALSE(all.DoesNothing());
+  EXPECT_TRUE(all.DoesAll());
+}
+
+TEST(SideEffectsTest, None) {
+  SideEffects none = SideEffects::None();
+  EXPECT_FALSE(none.DoesAnyWrite());
+  EXPECT_FALSE(none.DoesAnyRead());
+  EXPECT_TRUE(none.DoesNothing());
+  EXPECT_FALSE(none.DoesAll());
+}
+
+TEST(SideEffectsTest, DependencesAndNoDependences) {
+  // Apply test to each individual primitive type.
+  for (Primitive::Type type = Primitive::kPrimNot;
+      type < Primitive::kPrimVoid;
+      type = Primitive::Type(type + 1)) {
+    // Same primitive type and access type: proper write/read dep.
+    testWriteAndReadDependence(
+        SideEffects::FieldWriteOfType(type),
+        SideEffects::FieldReadOfType(type));
+    testWriteAndReadDependence(
+        SideEffects::ArrayWriteOfType(type),
+        SideEffects::ArrayReadOfType(type));
+    // Same primitive type but different access type: no write/read dep.
+    testNoWriteAndReadDependence(
+        SideEffects::FieldWriteOfType(type),
+        SideEffects::ArrayReadOfType(type));
+    testNoWriteAndReadDependence(
+        SideEffects::ArrayWriteOfType(type),
+        SideEffects::FieldReadOfType(type));
+  }
+}
+
+TEST(SideEffectsTest, NoDependences) {
+  // Different primitive type, same access type: no write/read dep.
+  testNoWriteAndReadDependence(
+      SideEffects::FieldWriteOfType(Primitive::kPrimInt),
+      SideEffects::FieldReadOfType(Primitive::kPrimDouble));
+  testNoWriteAndReadDependence(
+      SideEffects::ArrayWriteOfType(Primitive::kPrimInt),
+      SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
+  // Everything different: no write/read dep.
+  testNoWriteAndReadDependence(
+      SideEffects::FieldWriteOfType(Primitive::kPrimInt),
+      SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
+  testNoWriteAndReadDependence(
+      SideEffects::ArrayWriteOfType(Primitive::kPrimInt),
+      SideEffects::FieldReadOfType(Primitive::kPrimDouble));
+}
+
+TEST(SideEffectsTest, SameWidthTypes) {
+  // Type I/F.
+  testWriteAndReadDependence(
+      SideEffects::FieldWriteOfType(Primitive::kPrimInt),
+      SideEffects::FieldReadOfType(Primitive::kPrimFloat));
+  testWriteAndReadDependence(
+      SideEffects::ArrayWriteOfType(Primitive::kPrimInt),
+      SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
+  // Type L/D.
+  testWriteAndReadDependence(
+      SideEffects::FieldWriteOfType(Primitive::kPrimLong),
+      SideEffects::FieldReadOfType(Primitive::kPrimDouble));
+  testWriteAndReadDependence(
+      SideEffects::ArrayWriteOfType(Primitive::kPrimLong),
+      SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
+}
+
+TEST(SideEffectsTest, AllWritesAndReads) {
+  SideEffects s = SideEffects::None();
+  // Keep taking the union of different writes and reads.
+  for (Primitive::Type type = Primitive::kPrimNot;
+        type < Primitive::kPrimVoid;
+        type = Primitive::Type(type + 1)) {
+    s = s.Union(SideEffects::FieldWriteOfType(type));
+    s = s.Union(SideEffects::ArrayWriteOfType(type));
+    s = s.Union(SideEffects::FieldReadOfType(type));
+    s = s.Union(SideEffects::ArrayReadOfType(type));
+  }
+  EXPECT_TRUE(s.DoesAll());
+}
+
+TEST(SideEffectsTest, BitStrings) {
+  EXPECT_STREQ(
+      "|||||",
+      SideEffects::None().ToString().c_str());
+  EXPECT_STREQ(
+      "|DFJISCBZL|DFJISCBZL|DFJISCBZL|DFJISCBZL|",
+      SideEffects::All().ToString().c_str());
+  EXPECT_STREQ(
+      "||||L|",
+      SideEffects::FieldWriteOfType(Primitive::kPrimNot).ToString().c_str());
+  EXPECT_STREQ(
+      "|||Z||",
+      SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str());
+  EXPECT_STREQ(
+      "||B|||",
+      SideEffects::FieldReadOfType(Primitive::kPrimByte).ToString().c_str());
+  EXPECT_STREQ(
+      "|DJ||||",  // note: DJ alias
+      SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str());
+  SideEffects s = SideEffects::None();
+  s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar));
+  s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong));
+  s = s.Union(SideEffects::ArrayWriteOfType(Primitive::kPrimShort));
+  s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt));
+  s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
+  s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
+  EXPECT_STREQ(
+      "|DFJI|FI|S|DJC|",   // note: DJ/FI alias.
+      s.ToString().c_str());
+}
+
+}  // namespace art