[wasm-backend] Implement more comparison features.

This implements various small features needed for DeltaBlue:
- Comparison between two references
- Comparison between a reference and a literal null
- Assigning the result of a comparison to a bool variable
- Negating a bool
- Using a bool variable in a condition
- Mentioning dynamic (directly or indirectly)

Change-Id: I74c11cb180eb59e4bea66b751eafa59908325b2e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/166623
Reviewed-by: Tess Strickland <sstrickl@google.com>
diff --git a/runtime/vm/compiler/aot/wasm_codegen.cc b/runtime/vm/compiler/aot/wasm_codegen.cc
index 388cc0f..5a1f0ec 100644
--- a/runtime/vm/compiler/aot/wasm_codegen.cc
+++ b/runtime/vm/compiler/aot/wasm_codegen.cc
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 #include "vm/compiler/aot/wasm_codegen.h"
+#include "vm/object_store.h"
 
 #define Z (zone_)
 #define M (module_builder_)
@@ -191,8 +192,13 @@
 }
 
 WasmClassInfo& WasmCodegen::GetWasmClassInfo(const Class& klass) {
+  // Treat dynamic as Object.
+  Class& klass2 = Class::Handle(klass.raw());
+  if (klass.IsDynamicClass()) {
+    klass2 = Thread::Current()->isolate()->object_store()->object_class();
+  }
   pair<const Class*, WasmClassInfo>* const pair =
-      class_to_wasm_class_info_.Lookup(&klass);
+      class_to_wasm_class_info_.Lookup(&klass2);
   // At this point all classes should have been hoisted.
   // If this is not true, exit with an error even in non-debug builds.
   if (pair == nullptr) {
diff --git a/runtime/vm/compiler/aot/wasm_translator.cc b/runtime/vm/compiler/aot/wasm_translator.cc
index 0fa3979..d185f16 100644
--- a/runtime/vm/compiler/aot/wasm_translator.cc
+++ b/runtime/vm/compiler/aot/wasm_translator.cc
@@ -201,6 +201,15 @@
   VisitBinaryIntegerOp(instr);
 }
 
+void WasmTranslator::VisitBooleanNegate(BooleanNegateInstr* instr) {
+  PushValue(instr->value()->definition(),
+            AbstractType::Handle(AbstractType::null()));
+  wasm_scope_->AddI32Constant(1);
+  wasm_scope_->AddIntOp(wasm::IntOp::IntegerKind::kI32,
+                        wasm::IntOp::OpKind::kXor);
+  PopValue(instr);
+}
+
 void WasmTranslator::VisitParameter(ParameterInstr* instr) {
   // If the parameter is not the This parameter of a method, then just map
   // the parameter definition to the corresponding Wasm local of type kParam.
@@ -370,6 +379,21 @@
   wasm_scope_->AddStructSet(wasm_struct, wasm_field);
 }
 
+void WasmTranslator::VisitEqualityCompare(EqualityCompareInstr* instr) {
+  PushEvalCondition(instr, /*negated=*/false);
+  PopValue(instr);
+}
+
+void WasmTranslator::VisitStrictCompare(StrictCompareInstr* instr) {
+  PushEvalCondition(instr, /*negated=*/false);
+  PopValue(instr);
+}
+
+void WasmTranslator::VisitRelationalOp(RelationalOpInstr* instr) {
+  PushEvalCondition(instr, /*negated=*/false);
+  PopValue(instr);
+}
+
 intptr_t WasmTranslator::GetFallthroughPredecessorIndex(
     BlockEntryInstr* target) {
   intptr_t block_number = target->postorder_number();
@@ -582,8 +606,34 @@
     wasm_scope_->AddIntOp(wasm::IntOp::IntegerKind::kI64, op_kind);
   } else if (auto strict_comp_op =
                  comp->AsStrictCompare()) {  // General StrictCompare === !==
-    // TODO(askesc): Figure out the details.
-    UNIMPLEMENTED();
+    negated ^= comp->kind() == Token::Kind::kNE_STRICT;
+    if (IsBoolValue(comp->right())) {
+      if (auto const_instr = comp->right()->definition()->AsConstant()) {
+        const Object& value = const_instr->value();
+        const bool bool_value = Bool::Cast(value).value();
+        negated ^= !bool_value;
+        PushValue(comp->left()->definition(),
+                  *comp->left()->Type()->ToAbstractType());
+      } else {
+        UNIMPLEMENTED();
+      }
+    } else {
+      // To support comparison with a null constant on the right-hand side, we
+      // use the type of the left-hand side as the type hint for both operands.
+      // Alternatively, we could allow null constants without type hint and use
+      // the Object type in that case (which should be fine for comparisons).
+      PushValue(comp->left()->definition(),
+                *comp->left()->Type()->ToAbstractType());
+      PushValue(comp->right()->definition(),
+                *comp->left()->Type()->ToAbstractType());
+      wasm_scope_->AddRefEq();
+    }
+
+    if (negated) {
+      wasm_scope_->AddI32Constant(1);
+      wasm_scope_->AddIntOp(wasm::IntOp::IntegerKind::kI32,
+                            wasm::IntOp::OpKind::kXor);
+    }
   }
 }
 
diff --git a/runtime/vm/compiler/aot/wasm_translator.h b/runtime/vm/compiler/aot/wasm_translator.h
index 1e2590d..bbfb1e9 100644
--- a/runtime/vm/compiler/aot/wasm_translator.h
+++ b/runtime/vm/compiler/aot/wasm_translator.h
@@ -54,6 +54,7 @@
   virtual void VisitBinaryInt32Op(BinaryInt32OpInstr* instr);
   virtual void VisitBinaryInt64Op(BinaryInt64OpInstr* instr);
   virtual void VisitBinaryUint32Op(BinaryUint32OpInstr* instr);
+  virtual void VisitBooleanNegate(BooleanNegateInstr* instr);
   virtual void VisitParameter(ParameterInstr* instr);
   virtual void VisitStaticCall(StaticCallInstr* instr);
   virtual void VisitDispatchTableCall(DispatchTableCallInstr* instr);
@@ -61,6 +62,9 @@
   virtual void VisitAllocateObject(AllocateObjectInstr* instr);
   virtual void VisitLoadField(LoadFieldInstr* instr);
   virtual void VisitStoreInstanceField(StoreInstanceFieldInstr* instr);
+  virtual void VisitEqualityCompare(EqualityCompareInstr* instr);
+  virtual void VisitStrictCompare(StrictCompareInstr* instr);
+  virtual void VisitRelationalOp(RelationalOpInstr* instr);
 
  private:
   // Traverse graph to find start locations of Wasm blocks.
diff --git a/third_party/benchmarks/DeltaBlueSimplified.dart b/third_party/benchmarks/DeltaBlueSimplified.dart
index b432577..707d9e9 100644
--- a/third_party/benchmarks/DeltaBlueSimplified.dart
+++ b/third_party/benchmarks/DeltaBlueSimplified.dart
@@ -37,7 +37,7 @@
  * implementation.
  */
 
-main() {
+void main() {
   new DeltaBlue().run();
 }
 
@@ -90,6 +90,7 @@
 
   Constraint(this.planner, this.strength);
 
+  @pragma("vm:never-inline")
   bool isSatisfied();
   void markUnsatisfied();
   void addToGraph();
@@ -114,7 +115,7 @@
    * there is one, or nil, if there isn't.
    * Assume: I am not already satisfied.
    */
-  Constraint satisfy(mark) {
+  Constraint satisfy(int mark) {
     chooseMethod(mark);
     if (!isSatisfied()) {
       if (strength == REQUIRED) {
@@ -170,6 +171,7 @@
   }
 
   /// Returns true if this constraint is satisfied in the current solution.
+  @pragma("vm:never-inline")
   bool isSatisfied() => satisfied;
 
   void markInputs(int mark) {
@@ -286,6 +288,7 @@
   }
 
   /// Answer true if this constraint is satisfied in the current solution.
+  @pragma("vm:never-inline")
   bool isSatisfied() => direction != NONE;
 
   /// Mark the input variable with the given mark.
@@ -423,7 +426,7 @@
   /// Removes all traces of c from this variable.
   void removeConstraint(Constraint c) {
     constraints.remove(c);
-    if (determinedBy == c) determinedBy = null;
+    if (identical(determinedBy, c)) determinedBy = null;
   }
 }
 
@@ -739,7 +742,7 @@
     dummy.prev = dummy;
   }
 
-  bool get isEmpty => dummy.next == dummy;
+  bool get isEmpty => identical(dummy.next, dummy);
 
   void add(Constraint constraint) {
     ConstraintListElement element = new ConstraintListElement(constraint);
@@ -758,7 +761,7 @@
 
   void remove(Constraint constraint) {
     for (var elem = dummy.next; elem != dummy; elem = elem.next) {
-      if (elem.value == constraint) {
+      if (identical(elem.value, constraint)) {
         elem.remove();
         return;
       }