Open notcancername opened 1 year ago
I'm surprised to see this relabeled from documentation issue to implementation bug, so I'll write down my perspective/interpretation to maybe start the discussion of what the desired resolution should be.
Now the crux is this: To generalize short-circuiting operations and
and or
to vector elements, we need to decide on their impact on control flow.
I see 3 options for this (using example snippet bv and f()
):
f()
, then do the binary operation.f()
would be fully discarded.
This means we would need to decide control flow based on whether left-hand side bv
is fully true
for or
, and fully false
for and
.Currently the operation isn't implemented, and afaik what Zig should choose to do hasn't been decided yet. While 3 would be really neat for aesthetic reasons (i.e. sense of accomplishment), and although there might be advantages in certain scenarios, implementing that behavior would surely be a technical challenge (and I won't go into even more detail unprovoked).
I personally dislike option 1 for the reason that, if we allowed bitwise operators ~
, |
, and &
on bool
values with non-surprising semantics,
then they would be exactly equivalent to option 1 above.
Having both types of operators map to the same operation here, on top of maybe being surprising, nets us no benefit over just not supporting or
and and
on vectors,
which is why I'd personally prefer to see option 2 AND bitwise operations on bool
values implemented.
I think Option 1 Make them non-short-circuit is the best option.
I personally think of vector instructions like this:
z = x() + y();
// is conceptually equivalent to doing this:
var temp1 = x();
var temp2 = y();
z[0] = temp1[0] + temp2[0];
z[1] = temp1[1] + temp2[1];
...
When thinking like this the and
operator automatically becomes non-short-circuit:
z = x() and y();
// is conceptually equivalent to doing this:
var temp1 = x();
var temp2 = y();
z[0] = temp1[0] and temp2[0];
z[1] = temp1[1] and temp2[1];
...
So I would be really surprised if in a small number of cases y()
wasn't evaluated.
While Option 2 doesn't fit my mental model, maybe it's at least a useful optimization?
Let's consider the code x or f(y)
How likely is it that a single bool
x
is true
?
50% → we need to evaluate f(y)
50% of the time.
How likely is it that a @Vector(8, bool)
x
is true
in every field?
6% → we need to evaluate f(y)
94 % of the time.
This is not worth in my opinion. Especially considering that on arm @reduce()
takes a lot of operations.
So for small f(y)
short-circuiting would be significantly slower on arm.
While on the other hand even for the biggest possible f(y)
we can't get more than a 6% improvement on average.
And I don't think we need to consider option 3. It defeats the point of vector operations and it would be super complicated to implement.
The manual says that
Vectors support the same builtin operators as their underlying base types.
and
and or
are not operators, they're control flow keywords.
Making them not short circuit on vectors would be inconsistent with how they normally work, which is very confusing. As for having them do partial short circuiting, that's a potentially-expensive, hidden operation, which is very much in opposition to Zig's design.
My suggestion would be to make &
and |
work on bools and vectors of bools as non-short-circuiting operations, and keep and
and or
only for single bools.
and
andor
are not operators, they're control flow keywords.
and
and or
are listed in the table of operators, so I would assume that they are operators.
While they're parsed as operators, they explicitly do not have symbolic names like operators do, and they're not treated as operators internally (eg. in Zir they use a special bool_br
field rather than pl_node
with a Bin
payload like the binary operators do).
There have been issues about renaming them to &&
and ||
in the past, and the conclusion has been "they're named this way to distinguish them from operators, which don't do control flow"
To summarize, I believe these are proposed solutions so far:
and
and or
not short-circuit on Vectors.|
and &
and ~
/!
work on bools and bool vectors.and
and or
are not operators.The semantic effect (haven't checked any codegen) of non-short-circuiting and
and or
on vectors should be achievable using @select
:
const a = @Vector(4, bool){ true, false, true, false };
const b = @Vector(4, bool){ true, true, false, false};
const a_or_b = @select(bool, a, a, b);
const a_and_b = @select(bool, a, b, a);
However, this doesn't really seem to clearly communicate the intent to the compiler or reader to me, and takes a bit to understand especially with something like this
const a = @Vector(4, bool){ true, false, true, false };
const b = @Vector(4, bool){ true, true, false, false};
const any_true = @reduce(.Or, @select(bool, a, a, b)); // @reduce(.Or, a or b)
const all_true = @reduce(.And, @select(bool, a, b, a)); // @reduce(.And, a and b)
const all_true_per_lane = @reduce(.And, @select(bool, a, a, b)); // @reduce(.And, a or b)
const one_lane_both_true = @reduce(.Or, @select(bool, a, a, b)); // @reduce(.Or, a or b)
Zig Version
0.11.0-dev.1300+7cb2f9222
Steps to Reproduce and Observed Behavior
zig test
this code:An error is output:
Expected Behavior
The manual says that
This isn't the case for
and
. Based on the manual, I expected thatbv and ma
would evaluate to@Vector(4, bool){false, true, false, false}
.The same applies to
or
and!
.