operator>(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
  return !(lhs <= rhs);
}

bool operator>=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
  return !(lhs < rhs);
}

namespace {

class BinaryReaderOpcnt : public BinaryReaderNop {
 public:
  explicit BinaryReaderOpcnt(OpcodeInfoCounts* counts);

  Result OnOpcode(Opcode opcode) override;
  Result OnOpcodeBare() override;
  Result OnOpcodeUint32(uint32_t value) override;
  Result OnOpcodeIndex(Index value) override;
  Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override;
  Result OnOpcodeUint64(uint64_t value) override;
  Result OnOpcodeF32(uint32_t value) override;
  Result OnOpcodeF64(uint64_t value) override;
  Result OnOpcodeBlockSig(Type sig_type) override;
  Result OnBrTableExpr(Index num_targets,
                       Index* target_depths,
                       Index default_target_depth) override;
  Result OnEndExpr() override;
  Result OnEndFunc() override;

 private:
  template <typename... Args>
  Result Emplace(Args&&... args);

  OpcodeInfoCounts* opcode_counts_;
  Opcode current_opcode_;
};

template <typename... Args>
Result BinaryReaderOpcnt::Emplace(Args&&... args) {
  auto pair = opcode_counts_->emplace(
      std::piecewise_construct, std::make_tuple(std::forward<Args>(args)...),
      std::make_tuple(0));

  auto& count = pair.first->second;
  count++;
  return Result::Ok;
}

BinaryReaderOpcnt::BinaryReaderOpcnt(OpcodeInfoCounts* counts)
    : opcode_counts_(counts) {}

Result BinaryReaderOpcnt::OnOpcode(Opcode opcode) {
  current_opcode_ = opcode;
  return Result::Ok;
}

Result BinaryReaderOpcnt::OnOpcodeBare() {
  return Emplace(current_opcode_, OpcodeInfo::Kind::Bare);
}

Result BinaryReaderOpcnt::OnOpcodeUint32(uint32_t value) {
  return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32, &value);
}

Result BinaryReaderOpcnt::OnOpcodeIndex(Index value) {
  return Emplace(current_opcode_, OpcodeInfo::Kind::Index, &value);
}

Result BinaryReaderOpcnt::OnOpcodeUint32Uint32(uint32_t value0,
                            