core: Change RHS operand of bitshift operators to int32 #349
No reviewers
Labels
No Milestone
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: M-Labs/nac3#349
Loading…
Reference in New Issue
No description provided.
Delete Branch "issue-336"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #336.
9d9d71ffcb
toec350f0b44
WIP: core: Change RHS operand of bitshift operators to int32to core: Change RHS operand of bitshift operators to int32v2: Rebased against master
Is there a reason why it was done otherwise before?
This will also need changing the code in ARTIQ.
I have no clue, maybe @pca006132 or @ychenfo can pitch in. I just saw #336 and thought it would be a relatively trivial thing to address.
ec350f0b44
to4bdcecdf95
v3: Rebased against master
@ -303,0 +302,4 @@
let rhs = match lhs.get_type().get_bit_width().cmp(&rhs.get_type().get_bit_width()) {
Ordering::Less => self.builder.build_int_truncate(rhs, lhs.get_type(), ""),
Ordering::Equal => rhs,
Ordering::Greater => self.builder.build_int_z_extend(rhs, lhs.get_type(), ""),
AFAICT this will break silently with negative shifts.
Python disallows them:
So the RHS type should perhaps be uint32 below instead of int32, and we should check that the compiler reports a sensible error if the user enters a negative value there.
But I worry that uint32 would require explicit casts and a lot of typing, e.g.
1 << uint32(1)
instead of just1 << 1
?Alternatively we can simply support negative shifts since this is just a strict superset of Python as far as I can tell. But the zero-extend here should be replaced with a sign extend.
4bdcecdf95
to5d28e4e780
core: Change RHS operand of bitshift operators to int32to WIP: core: Change RHS operand of bitshift operators to int325d28e4e780
to09dbae1e63
v4, v5: Changed bitshift operators to accept both the type of the LHS operand and
int32
, added compile-time and runtime error checkingWIP: core: Change RHS operand of bitshift operators to int32to core: Change RHS operand of bitshift operators to int32That sounds like unnecessary complications. Why not just int32?
This will cause the
operators.py
test to require casts on all bitwise shifts, such as:It seems to me that there's some redundancy there, as the maximum RHS bitshift value of
0..63
can be represented by any integral type supported by NAC3.Sounds fine to me. It's just a bit of simple code into one test.
And it makes it clear to someone reading the test that nac3 expects int32 there.
After changing it to cast to int32 I got this error:
It seems like NumPy doesn't allow the bitshift operator with
uint64
andint32
as its types, all other cases (int32
/int64
/uint32
) work.Yes this can be reproduced easily:
Also without int32:
Apart from allowing both
int32
and the LHS operand type, do you have other suggestions on how to workaround this issue?Let's try to understand first why numpy does it like this, seems it's not just a numpy bug.
And I don't see how this resolves anything (other than hide the issue somewhat in the test) since nac3 would still allow
np.uint64(1) << 1
but not numpy.It seems like this issue is documented here: https://github.com/numpy/numpy/issues/22624 and https://github.com/numpy/numpy/issues/2524.
np._set_promotion_state("weak")
may be a fix (just like we already needfrom __future__ import annotations
)https://numpy.org/neps/nep-0050-scalar-promotion.html
How about directly hardcoding the behavior of NumPy, i.e. allowing
int32
RHS operands toint32
/uint32
/int64
RHS operands but only allowinguint64
foruint64
LHS operand?Doesn't seem like people are happy with the numpy behavior as evidenced by the links above. Having int32 only is a simple solution which AFAICT alleviates legitimate concerns, and if numpy acts up simply use
_set_promotion_state
to get the behavior that numpy are planning to implement.And this will only matter in
@portable
code that shifts unsigned integers i.e. a minor use case.The issue right now is that
_set_promotion_state
doesn't seem to affect bitwise shift operators as evident from the interpreter snippet.Actually it does but incompletely...
Maybe we can support both int32 and uint32?
Then:
_set_promotion_state
and/or verbose type conversions.How about we only accept
int32
/uint32
depending on the signness of the left operand? It seems like NAC3's unifier does not support overloading for the same operator.09dbae1e63
to685475c6f5
v6: Rebased against master, require bitshift operands to match in sign-ness.
Does this make any sense from the user's point of view?
Honestly speaking, I don't really see a good solution for this issue without having to extend the type inferencer to support function overloads or union types...
Requiring matching signness is probably the best solution without having to mess with the inferencer.
685475c6f5
to856b4fb371
v7: Allow only
int32
anduint32
on RHS operand of bitshift operator@ -302,1 +301,3 @@
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
(Operator::LShift, signed) | (Operator::RShift, signed) => {
let common_type = lhs.get_type();
let rhs = if common_type.get_bit_width() > rhs.get_type().get_bit_width() {
RHS is always 32, no?
@ -303,0 +307,4 @@
self.builder.build_int_z_extend(rhs, common_type, "")
}
} else {
self.builder.build_int_truncate_or_bit_cast(rhs, common_type, "")
Do we ever do this? Are there any int types smaller than 32-bit?
@ -303,0 +310,4 @@
self.builder.build_int_truncate_or_bit_cast(rhs, common_type, "")
};
let rhs_gez = self.builder.build_int_compare(IntPredicate::SGE, rhs, common_type.const_zero(), "");
Is LLVM able to optimize this away when the RHS is uint32?
Or a constant?
Yes, all test cases can be constant-folded using optimizations.
@ -300,3 +300,2 @@
(Operator::BitAnd, _) => self.builder.build_and(lhs, rhs, "and").into(),
(Operator::LShift, _) => self.builder.build_left_shift(lhs, rhs, "lshift").into(),
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
(Operator::LShift, signed) | (Operator::RShift, signed) => {
It's become unclear here to which side of the operand
signed
refers to. I suggest adding a comment here to clarify what is going on.856b4fb371
to62790ce974
v8: Minor code cleanup