Upgrade to C++20

This will enable us to use new features including concepts and coroutines to
make our code easier to read and write.

In order to make the build work after the update, remove many newly unnecessary
overloads of operator!=, which older C++ compilers consider to be ambiguous with
their corresponding overloads of operator==, despite this having already been
fixed in the C++ spec. The operator!= overloads are not necessary in C++20
because the compiler can synthesize them automatically.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a008b5..a7723d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,7 +13,7 @@
 # The C++ standard whose features are required to build Binaryen.
 # Keep in sync with scripts/test/shared.py cxx_standard
 # The if condition allows embedding in a project with a higher default C++ standard set
-set(REQUIRED_CXX_STANDARD 17)
+set(REQUIRED_CXX_STANDARD 20)
 if(NOT CMAKE_CXX_STANDARD)
   set(CMAKE_CXX_STANDARD ${REQUIRED_CXX_STANDARD})
 elseif(CMAKE_CXX_STANDARD LESS ${REQUIRED_CXX_STANDARD})
diff --git a/README.md b/README.md
index 130dbf9..8e06420 100644
--- a/README.md
+++ b/README.md
@@ -342,7 +342,7 @@
 cmake . && make
 ```
 
-A C++17 compiler is required. Note that you can also use `ninja` as your generator: `cmake -G Ninja . && ninja`.
+A C++20 compiler is required. Note that you can also use `ninja` as your generator: `cmake -G Ninja . && ninja`.
 
 To avoid the gtest dependency, you can pass `-DBUILD_TESTS=OFF` to cmake.
 
diff --git a/src/dataflow/node.h b/src/dataflow/node.h
index 10e7817..3578b30 100644
--- a/src/dataflow/node.h
+++ b/src/dataflow/node.h
@@ -167,7 +167,7 @@
     }
   }
 
-  bool operator==(const Node& other) {
+  bool operator==(const Node& other) const {
     if (type != other.type) {
       return false;
     }
@@ -200,8 +200,6 @@
     return true;
   }
 
-  bool operator!=(const Node& other) { return !(*this == other); }
-
   // As mentioned above, comparisons return i1. This checks
   // if an operation is of that sort.
   bool returnsI1() {
diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp
index 086d504..dd0b59f 100644
--- a/src/emscripten-optimizer/simple_ast.cpp
+++ b/src/emscripten-optimizer/simple_ast.cpp
@@ -28,16 +28,10 @@
   return get()->isString() && get()->str == str;
 }
 
-bool Ref::operator!=(std::string_view str) { return !(*this == str); }
-
 bool Ref::operator==(const IString& str) {
   return get()->isString() && get()->str == str;
 }
 
-bool Ref::operator!=(const IString& str) {
-  return get()->isString() && get()->str != str;
-}
-
 bool Ref::operator==(Ref other) { return **this == *other; }
 
 bool Ref::operator!() { return !get() || get()->isNull(); }
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index e4c886d..dbe2048 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -65,9 +65,7 @@
   // special conveniences
   bool
   operator==(std::string_view str); // comparison to string, which is by value
-  bool operator!=(std::string_view str);
   bool operator==(const IString& str);
-  bool operator!=(const IString& str);
   // prevent Ref == number, which is potentially ambiguous; use ->getNumber() ==
   // number
   bool operator==(double d) {
@@ -287,7 +285,7 @@
     return *this;
   }
 
-  bool operator==(const Value& other) {
+  bool operator==(const Value& other) const {
     if (type != other.type) {
       return false;
     }
diff --git a/src/support/parent_index_iterator.h b/src/support/parent_index_iterator.h
index e19aacf..159df1c 100644
--- a/src/support/parent_index_iterator.h
+++ b/src/support/parent_index_iterator.h
@@ -52,9 +52,6 @@
   bool operator==(const ParentIndexIterator& other) const {
     return index == other.index && parent == other.parent;
   }
-  bool operator!=(const ParentIndexIterator& other) const {
-    return !(*this == other);
-  }
   Iterator& operator++() {
     ++index;
     return self();
diff --git a/src/support/small_vector.h b/src/support/small_vector.h
index 4f2dc0b..93585c5 100644
--- a/src/support/small_vector.h
+++ b/src/support/small_vector.h
@@ -141,10 +141,6 @@
     return flexible == other.flexible;
   }
 
-  bool operator!=(const SmallVector<T, N>& other) const {
-    return !(*this == other);
-  }
-
   // iteration
 
   template<typename Parent, typename Iterator> struct IteratorBase {
@@ -161,10 +157,6 @@
 
     IteratorBase(Parent* parent, size_t index) : parent(parent), index(index) {}
 
-    bool operator!=(const Iterator& other) const {
-      return index != other.index || parent != other.parent;
-    }
-
     Iterator& operator++() {
       Iterator& self = *static_cast<Iterator*>(this);
       index++;
@@ -187,7 +179,7 @@
 
     off_t operator-(const Iterator& other) const { return index - other.index; }
 
-    bool operator==(const Iterator& other) const {
+    bool operator==(const IteratorBase& other) const {
       return parent == other.parent && index == other.index;
     }
   };
diff --git a/src/support/topological_sort.h b/src/support/topological_sort.h
index 91353dd..18dba11 100644
--- a/src/support/topological_sort.h
+++ b/src/support/topological_sort.h
@@ -93,8 +93,9 @@
     TopologicalSort<T, Subtype>* parent;
 
     bool isEnd() const { return !parent || parent->workStack.empty(); }
-    bool operator==(Iterator& other) const { return isEnd() == other.isEnd(); }
-    bool operator!=(Iterator& other) const { return !(*this == other); }
+    bool operator==(const Iterator& other) const {
+      return isEnd() == other.isEnd();
+    }
     T operator*() { return parent->workStack.back(); }
     void operator++(int) {
       parent->finishCurr();
@@ -110,7 +111,7 @@
     stepToNext();
     return {this};
   }
-  Iterator end() { return {nullptr}; }
+  Iterator end() const { return {nullptr}; }
 };
 
 } // namespace wasm
diff --git a/third_party/llvm-project/include/llvm/ADT/StringMap.h b/third_party/llvm-project/include/llvm/ADT/StringMap.h
index 108185b..91a7ea7 100644
--- a/third_party/llvm-project/include/llvm/ADT/StringMap.h
+++ b/third_party/llvm-project/include/llvm/ADT/StringMap.h
@@ -505,7 +505,7 @@
     return static_cast<DerivedTy &>(*this);
   }
 
-  bool operator==(const DerivedTy &RHS) const { return Ptr == RHS.Ptr; }
+  bool operator==(const StringMapIterBase &RHS) const { return Ptr == RHS.Ptr; }
 
   DerivedTy &operator++() { // Preincrement
     ++Ptr;
diff --git a/third_party/llvm-project/include/llvm/ADT/iterator.h b/third_party/llvm-project/include/llvm/ADT/iterator.h
index 467fd4c..1d00e1e 100644
--- a/third_party/llvm-project/include/llvm/ADT/iterator.h
+++ b/third_party/llvm-project/include/llvm/ADT/iterator.h
@@ -142,10 +142,6 @@
     return tmp;
   }
 
-  bool operator!=(const DerivedT &RHS) const {
-    return !static_cast<const DerivedT *>(this)->operator==(RHS);
-  }
-
   bool operator>(const DerivedT &RHS) const {
     static_assert(
         IsRandomAccess,