[dart2js] GVN string concatenation

GVN pure stringifiers and string concatenation.

I removed the comment regarding #9293. I could not understand it.
Removing the 'setDependsOnSomething()' does occasionally produce
worse-looking code, but at the application level, the code is
marginally smaller.

https://github.com/dart-lang/sdk/issues/48243 is a more general
description of the 'looks better' problem that pure operations tend to
sink to their use, increasing live-ranges of the operands.  The
sinking is sometimes advantageous - e.g. when the new location is on
an error path.

I see common subexpression elimination often in the pattern of
constructing default strings for Intl.plural:


    Intl.plural(...,
        one: '$first (+$howManyMore language)',
        other: '$first (+$howManyMore languages)',
        ...);

--> old JavaScript

    var
      t1 = A.S(first) + " (+" + howManyMore + " language)",
      t2 = A.S(first) + " (+" + howManyMore + " languages)";
    return A.Intl__plural(..., t1, t2, ...);

--> new JavaScript

    var
      t1 = A.S(first) + " (+" + howManyMore;
    return A.Intl__plural(..., t1 + " language)", t1 + " languages)", ...);

Change-Id: I56aff26c9e5e31954fef224378f3723a05b318ea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/233641
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index fa96a89..2026528 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1096,6 +1096,8 @@
   static const int TYPE_BIND_TYPECODE = 57;
 
   static const int IS_LATE_SENTINEL_TYPECODE = 58;
+  static const int STRING_CONCAT_TYPECODE = 59;
+  static const int STRINGIFY_TYPECODE = 60;
 
   HInstruction(this.inputs, this.instructionType) {
     assert(inputs.every((e) => e != null), "inputs: $inputs");
@@ -3742,10 +3744,7 @@
 class HStringConcat extends HInstruction {
   HStringConcat(HInstruction left, HInstruction right, AbstractValue type)
       : super([left, right], type) {
-    // TODO(sra): Until Issue 9293 is fixed, this false dependency keeps the
-    // concats bunched with stringified inputs for much better looking code with
-    // fewer temps.
-    sideEffects.setDependsOnSomething();
+    setUseGvn();
   }
 
   HInstruction get left => inputs[0];
@@ -3755,6 +3754,13 @@
   accept(HVisitor visitor) => visitor.visitStringConcat(this);
   @override
   toString() => "string concat";
+
+  @override
+  int typeCode() => HInstruction.STRING_CONCAT_TYPECODE;
+  @override
+  bool typeEquals(HInstruction other) => other is HStringConcat;
+  @override
+  bool dataEquals(HStringConcat other) => true;
 }
 
 /// The part of string interpolation which converts and interpolated expression
@@ -3771,6 +3777,7 @@
     sideEffects.clearAllDependencies();
     sideEffects.clearAllSideEffects();
     _isPure = true;
+    setUseGvn();
   }
 
   @override
@@ -3780,6 +3787,13 @@
   accept(HVisitor visitor) => visitor.visitStringify(this);
   @override
   toString() => "stringify";
+
+  @override
+  int typeCode() => HInstruction.STRINGIFY_TYPECODE;
+  @override
+  bool typeEquals(HInstruction other) => other is HStringify;
+  @override
+  bool dataEquals(HStringify other) => this._isPure == other._isPure;
 }
 
 /// Non-block-based (aka. traditional) loop information.