MINOR: thread: keep a bitmask of enabled groups in thread_set

We're only checking for 0, 1, or >1 groups enabled there, and we'll soon
need to be more precise and know quickly which groups are non-empty.
Let's just replace the count with a mask of enabled groups. This will
allow to quickly spot the presence of any such group in a set.
diff --git a/include/haproxy/tinfo-t.h b/include/haproxy/tinfo-t.h
index 7c201af..ce893ca 100644
--- a/include/haproxy/tinfo-t.h
+++ b/include/haproxy/tinfo-t.h
@@ -40,7 +40,7 @@
 		ulong abs[(MAX_THREADS + LONGBITS - 1) / LONGBITS];
 		ulong rel[MAX_TGROUPS];
 	};
-	uint nbgrp; /* number of non-empty groups in this set, 0 for abs */
+	ulong grps; /* bit field of all non-empty groups, 0 for abs */
 };
 
 /* tasklet classes */
diff --git a/include/haproxy/tinfo.h b/include/haproxy/tinfo.h
index 3d10560..ddb26aa 100644
--- a/include/haproxy/tinfo.h
+++ b/include/haproxy/tinfo.h
@@ -77,7 +77,7 @@
 {
 	int i;
 
-	if (ts->nbgrp) {
+	if (ts->grps) {
 		for (i = 0; i < MAX_TGROUPS; i++)
 			if (ts->rel[i] && !n--)
 				return i + 1;
@@ -95,7 +95,7 @@
 {
 	int i;
 
-	if (ts->nbgrp) {
+	if (ts->grps) {
 		for (i = 0; i < MAX_TGROUPS; i++)
 			if (ts->rel[i] && !n--)
 				return ts->rel[i];
@@ -111,7 +111,7 @@
 {
 	int i;
 
-	ts->nbgrp = 1;
+	ts->grps = 1;
 	ts->rel[0] = mask;
 	for (i = 1; i < MAX_TGROUPS; i++)
 		ts->rel[i] = 0;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 52f40d6..4272c3d 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2998,18 +2998,16 @@
 						bit = (bind_conf->thread_set.rel[grp] & ~mask) & -(bind_conf->thread_set.rel[grp] & ~mask);
 						new_ts.rel[grp] |= bit;
 						mask |= bit;
-
-						if (!atleast2(new_ts.rel[grp])) // first time we add a bit: new group
-							new_ts.nbgrp++;
+						new_ts.grps |= 1UL << grp;
 
 						done += shards;
 					};
 
-					BUG_ON(!new_ts.nbgrp); // no more bits left unassigned
+					BUG_ON(!new_ts.grps); // no more bits left unassigned
 
-					if (new_ts.nbgrp > 1) {
+					if (atleast2(new_ts.grps)) {
 						ha_alert("Proxy '%s': shard number %d spans %d groups in 'bind %s' at [%s:%d]\n",
-							 curproxy->id, shard, new_ts.nbgrp, bind_conf->arg, bind_conf->file, bind_conf->line);
+							 curproxy->id, shard, my_popcountl(new_ts.grps), bind_conf->arg, bind_conf->file, bind_conf->line);
 						cfgerr++;
 						err_code |= ERR_FATAL | ERR_ALERT;
 						goto out;
@@ -4450,7 +4448,7 @@
 						free(err);
 						cfgerr++;
 					}
-					else if (bind_conf->thread_set.nbgrp > 1) {
+					else if (atleast2(bind_conf->thread_set.grps)) {
 						ha_alert("Peers section '%s': 'thread' spans more than one group in 'bind %s' at [%s:%d].\n",
 							 curpeers->peers_fe->id, bind_conf->arg, bind_conf->file, bind_conf->line);
 						cfgerr++;
diff --git a/src/thread.c b/src/thread.c
index d11df8d..84d39d7 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -1240,7 +1240,7 @@
 	ulong mask, imask;
 	uint g;
 
-	if (!ts->nbgrp) {
+	if (!ts->grps) {
 		/* unspecified group, IDs are global */
 		if (thread_set_is_empty(ts)) {
 			/* all threads of all groups, unless defgrp is set and
@@ -1248,7 +1248,8 @@
 			 */
 			for (g = defgrp ? defgrp-1 : 0; g < (defgrp ? defgrp : global.nbtgroups); g++) {
 				new_ts.rel[g] = ha_tgroup_info[g].threads_enabled;
-				new_ts.nbgrp++;
+				if (new_ts.rel[g])
+					new_ts.grps |= 1UL << g;
 			}
 		} else {
 			/* some absolute threads are set, we must remap them to
@@ -1271,9 +1272,9 @@
 				/* now the mask exactly matches the threads to be enabled
 				 * in this group.
 				 */
-				if (!new_ts.rel[g] && mask)
-					new_ts.nbgrp++;
 				new_ts.rel[g] |= mask;
+				if (new_ts.rel[g])
+					new_ts.grps |= 1UL << g;
 			}
 		}
 	} else {
@@ -1310,7 +1311,8 @@
 			}
 
 			new_ts.rel[g] = imask & mask;
-			new_ts.nbgrp++;
+			if (new_ts.rel[g])
+				new_ts.grps |= 1UL << g;
 		}
 	}
 
@@ -1521,8 +1523,7 @@
 		if (ts) {
 			if (is_rel) {
 				/* group-relative thread numbers */
-				if (!ts->rel[tg - 1])
-					ts->nbgrp++;
+				ts->grps |= 1UL << (tg - 1);
 
 				if (max >= min) {
 					for (v = min; v <= max; v++)