Add synthetic hashing to CF instructions.

Bug: b/237413146
Change-Id: I887f6722b73382caaebde80f483f8142fbd14ee4
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index 2c2c461..95cdc02 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -58,6 +59,11 @@
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
   public Opcode getOpcode() {
     return opcode;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 6562ee0..a78ca40 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -57,6 +58,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void print(CfPrinter printer) {
     printer.print(this);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoadOrStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoadOrStore.java
index 93dd67a..97b4ba4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoadOrStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoadOrStore.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 
 public abstract class CfArrayLoadOrStore extends CfInstruction {
 
@@ -62,4 +63,9 @@
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
+
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index a45285b..6c30ad3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -79,6 +80,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index 4486ac5..6c1eea5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -55,6 +56,11 @@
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
   public Bias getBias() {
     return bias;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index b61202a..e8019d9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
@@ -62,6 +63,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
+  @Override
   public CfTypeInstruction asTypeInstruction() {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
index 4bff728..6c379bb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.ListIterator;
@@ -112,16 +113,12 @@
   @Override
   public int internalAcceptCompareTo(
       CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
-    int diff = getName().acceptCompareTo(((CfConstDynamic) other).getName(), visitor);
-    if (diff != 0) {
-      return diff;
-    }
-    diff = getType().acceptCompareTo(((CfConstDynamic) other).getType(), visitor);
-    if (diff != 0) {
-      return diff;
-    }
-    return getBootstrapMethod()
-        .acceptCompareTo(((CfConstDynamic) other).getBootstrapMethod(), visitor);
+    return reference.acceptCompareTo(((CfConstDynamic) other).reference, visitor);
+  }
+
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    reference.acceptHashing(visitor);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index c58e97a..ca13ef3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
@@ -52,6 +53,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    handle.acceptHashing(visitor);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 69a82a9..2c8a35f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
@@ -52,6 +53,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index 978b9df..a77dc2c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -57,6 +58,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void print(CfPrinter printer) {
     printer.print(this);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index c5841e8..cf6d035 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -53,6 +54,11 @@
     return visitor.visit(this, (CfConstNumber) other, CfConstNumber::specify);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, CfConstNumber::specify);
+  }
+
   public ValueType getType() {
     return type;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index a1cf1b8..46f1c0c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfConstString extends CfInstruction {
@@ -43,6 +44,11 @@
     return string.acceptCompareTo(other.asConstString().string, visitor);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    string.acceptHashing(visitor);
+  }
+
   public DexString getString() {
     return string;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index 7d24f4b..abc1113 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
@@ -50,6 +51,11 @@
     return visitor.visitDexReference(item, ((CfDexItemBasedConstString) other).item);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visitDexReference(item);
+  }
+
   public DexReference getItem() {
     return item;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 9ff6d09..1709270 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -77,6 +78,11 @@
     return visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, CfFieldInstruction::specify);
+  }
+
   public abstract CfFieldInstruction createWithField(DexField field);
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 6b2ee79..dc25d98 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.utils.IntObjConsumer;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.google.common.collect.Iterables;
 import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -73,6 +74,11 @@
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
   private final Int2ObjectSortedMap<FrameType> locals;
   private final Deque<PreciseFrameType> stack;
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index ac161ae..7df905c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -46,6 +47,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // We have no label identity to hash based on.
+  }
+
+  @Override
   public CfGoto asGoto() {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index 5858c6f..ffb1329 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -47,6 +48,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // The compare-id distinguishes types and we have no label identity to use.
+  }
+
+  @Override
   public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
       BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
       CfInstruction fallthroughInstruction,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index 63ff9ca..883ee0e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -47,6 +48,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // The compare-id distinguishes types and we have no label identity to use.
+  }
+
+  @Override
   public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
       BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
       CfInstruction fallthroughInstruction,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 164bf07..570a842 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.FunctionUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -54,6 +55,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, CfIinc::specify);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index 39735cb..7a54a7b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 
@@ -58,6 +59,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    clazz.acceptHashing(visitor);
+  }
+
+  @Override
   public boolean isInitClass() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index d13c3a9..4feb794 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -68,6 +69,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
+  @Override
   public boolean isInstanceOf() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index bc8e8fb..b137991 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -84,6 +85,13 @@
     return internalAcceptCompareTo(o, visitor, helper);
   }
 
+  public abstract void internalAcceptHashing(HashingVisitor visitor);
+
+  public final void acceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(getCompareToId());
+    internalAcceptHashing(visitor);
+  }
+
   @Override
   public String toString() {
     CfPrinter printer = new CfPrinter();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 2be9f2e..285ebe5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.Arrays;
 import java.util.ListIterator;
@@ -72,6 +73,11 @@
     return visitor.visit(this, otherInvoke, CfInvoke::specify);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, CfInvoke::specify);
+  }
+
   public DexMethod getMethod() {
     return method;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index b880c47..121305c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.ListIterator;
@@ -68,6 +69,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    callSite.acceptHashing(visitor);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index 4eaeca8..db045cd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfJsrRet extends CfInstruction {
@@ -49,6 +50,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    throw error();
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 4bd6f52..9121e85 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 
@@ -47,6 +48,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // No label identity to use.
+  }
+
+  @Override
   public CfLabel asLabel() {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index 9992cc8..f18c47c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.optimize.interfaces.analysis.ErroneousCfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -50,6 +51,11 @@
     return visitor.visitInt(var, other.asLoad().var);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(var);
+  }
+
   private int getLoadType() {
     switch (type) {
       case OBJECT:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index a5f499d..a8333d6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -60,6 +61,11 @@
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
   public NumericType getType() {
     return type;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index db26e94..98a73e5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -50,6 +51,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 672db34..1fdb693 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
@@ -80,6 +81,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, CfMultiANewArray::specify);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index 0cee97d..d6f9c18 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -51,6 +52,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 00fb189..bbefd75 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -86,6 +87,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 68494c8..aa3d852 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -71,6 +72,11 @@
     return type.acceptCompareTo(((CfNewArray) other).type, visitor);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
   private int getPrimitiveTypeCode() {
     switch (type.descriptor.content[1]) {
       case 'Z':
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
index 4b759a2..e6b8170 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.ListIterator;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -72,6 +73,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    type.acceptHashing(visitor);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index b5d7d56..f4b0527 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -38,6 +39,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index 40f7298..a8a88b6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -52,6 +53,11 @@
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
   public NumericType getFromType() {
     return from;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 6e7dc78..ec74503 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfPosition extends CfInstruction {
@@ -51,6 +52,12 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(position.getLine());
+    // No label identity to add.
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java b/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
index 794872f..dd9c957 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import org.objectweb.asm.MethodVisitor;
@@ -90,6 +91,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, CfRecordFieldValues::specify);
+  }
+
+  @Override
   public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     int parameterCount = fields.length;
     int[] registers = new int[parameterCount];
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 8fb0024..ee0f453 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -52,6 +53,11 @@
     return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
   private int getOpcode() {
     switch (type) {
       case INT:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index 2a25414..497ad00 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -48,6 +49,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index 664e746..2640c16 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.FunctionUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -96,6 +97,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index 6439925..3ff52f9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -48,6 +49,11 @@
     return visitor.visitInt(var, other.asStore().var);
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(var);
+  }
+
   private int getStoreType() {
     switch (type) {
       case OBJECT:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index 75ff36d..2492110 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.TraversalUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import java.util.List;
 import java.util.function.BiFunction;
@@ -78,6 +79,15 @@
                 .withCustomItemCollection(CfSwitch::getSwitchTargets, helper.labelAcceptor()));
   }
 
+  @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(keys.length);
+    for (int i = 0; i < keys.length; i++) {
+      visitor.visitInt(keys[i]);
+    }
+    // No label identity info to add.
+  }
+
   public Kind getKind() {
     return kind;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index bf50856..7333669 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -49,6 +50,11 @@
   }
 
   @Override
+  public void internalAcceptHashing(HashingVisitor visitor) {
+    // Nothing to add.
+  }
+
+  @Override
   public boolean isThrow() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 9da9fd8..f415e4b 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -353,15 +353,10 @@
 
   @Override
   public void acceptHashing(HashingVisitor visitor) {
-    // Rather than hash the entire content, hash the sizes and each instruction "type" which
-    // should provide a fast yet reasonably distinct key.
-    // TODO(b/158159959): This will likely lead to a lot of distinct synthetics hashing to the same
-    //  hash as many have the same instruction pattern such as an invoke of the impl method or a
-    //  field access.
     visitor.visitInt(instructions.size());
     visitor.visitInt(tryCatchRanges.size());
     visitor.visitInt(localVariables.size());
-    instructions.forEach(i -> visitor.visitInt(i.getCompareToId()));
+    instructions.forEach(i -> i.acceptHashing(visitor));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
index 43d76c7..ed08c4d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
@@ -7,15 +7,26 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.utils.structural.Equatable;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.List;
 import java.util.Objects;
 
-public class ConstantDynamicReference {
+public class ConstantDynamicReference implements StructuralItem<ConstantDynamicReference> {
   private final DexString name;
   private final DexType type;
   private final DexMethodHandle bootstrapMethod;
   private final List<DexValue> bootstrapMethodArguments;
 
+  private static void specify(StructuralSpecification<ConstantDynamicReference, ?> spec) {
+    spec.withItem(ConstantDynamicReference::getName)
+        .withItem(ConstantDynamicReference::getType)
+        .withItem(ConstantDynamicReference::getBootstrapMethod)
+        .withItemCollection(ConstantDynamicReference::getBootstrapMethodArguments);
+  }
+
   public ConstantDynamicReference(
       DexString name,
       DexType type,
@@ -28,6 +39,16 @@
     this.bootstrapMethodArguments = bootstrapMethodArguments;
   }
 
+  @Override
+  public ConstantDynamicReference self() {
+    return this;
+  }
+
+  @Override
+  public StructuralMapping<ConstantDynamicReference> getStructuralMapping() {
+    return ConstantDynamicReference::specify;
+  }
+
   public DexString getName() {
     return name;
   }
@@ -46,18 +67,11 @@
 
   @Override
   public boolean equals(Object obj) {
-    if (this == obj) return true;
-    if (!(obj instanceof ConstantDynamicReference)) {
-      return false;
-    }
-    ConstantDynamicReference other = (ConstantDynamicReference) obj;
-    return Objects.equals(name, other.name)
-        && Objects.equals(type, other.type)
-        && Objects.equals(bootstrapMethod, other.bootstrapMethod);
+    return Equatable.equalsImpl(this, obj);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(name, type, bootstrapMethod);
+    return Objects.hash(name, type, bootstrapMethod, bootstrapMethodArguments);
   }
 }