Attempt to optimize base eval mustbeequal layer
While trying to squeeze maximum performance out of #796, I at some point completely removed this layer for some speedup I guess, but not to remove features, this is an attempt to slightly optimize it instead.
The point being that when base evals some binary operators (comparisons and minus), then the mustbeequal layer first queries whether they must be exactly equal (this was refactored in #770, but the idea is still the same). If so, then some nice definite value can be returned immediately, which base's structural eval layer wouldn't do, unless the values are definite.
However, I was under the impression that this has a performance penalty in the much more likely case that they are not equal. Namely, base is also answering that mustbeequal check itself, so first evaluating some x == y, which then is unknown and then falling back to evaluating x <= y or whatever, duplicating some work.
So the optimization here is to switch the two around: evaluate normally first and if that's not definite, try mustbeequal as a fallback for the rare cases it helps.
After benchmarking on sv-benchmark, it turns out this "optimization" doesn't really make any difference:
So I'm not sure if we should just close this PR or keep it for a little bit of refactoring it does, moving the mustbeequal check down to where all the other binop evaluation is happening. I guess the optimizations from #590 and #770 are already enough to practically avoid this supposed double work.
There's one case this might still make some difference: if Apron is enabled, then it avoids a more expensive Apron query if base itself already can find a definite answer. Or in another way, if some x <= y is evaluated, Apron is also queried for that directly, so even if then querying Apron for x == y would give a definite answer, it would give that answer to the initial x <= y query as well.
As far as I remember, these mustbeequal layer things have very little benefit. Only a single test specifically added in #770 requires them to pass, all the partitioned array ones work without as well (probably because they just need exprelation for the equalites it handles, not other comparisons and subtractions). But that's a separate discussion whether to remove them altogether.