bitwise followup #449
No reviewers
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
refactor
rust
technical-debt
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/patch-seq!449
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bitwise"
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?
⏺ Done. Summary:
Three codegen paths to keep in sync — discovered mid-implementation:
through it; the others are inlined).
(codegen_shift_compute, codegen_inline_int_unary_intrinsic).
— register-based specialization path (emit_specialized_safe_shift, popcount/clz/ctz).
All three now apply the same 63-bit clamp on shl/shr (out-of-range → 0) and the same
63-bit-aware adjustments on popcount (mask off bit 63), clz (saturating −1 of the i64
count), ctz (special-case v == 0 → 63), and int_bits (returns 63).
Tests:
test-clz-ctz-edge-cases (63 instead of 64), test-popcount-edge-cases
(popcount(-1)=63). Added test-shl-overflow (boundary cases at 1 61 shl, 1 62 shl, -1
62 shl, -1 63 shl) and test-shr-negative (-1 1 shr=0, -1 2 shr=I63_MAX, -1 62 shr=3).
test-int-to-bits-of-negative-terminates (the original infinite-loop regression check).
Docs:
value, replaced the broken -1 1 shr example with two truthful ones (1 61 shl
2305843009213693952, 1 62 shl 0, -1 2 shr 2^62-1, -1 1 shr 0), and rewrote the Shift
Behavior bullets.
continues to work unchanged because int-bits and clz moved together (verified by
running it: outputs 0,1,1,2,2,3,6 for inputs 1,2,3,4,7,8,100).
produce lossy output (rather than "will not terminate").
just ci green: fmt, clippy -D warnings, all unit/integration/example tests, seq lint.
Reviewed against
docs/design/TAGGED_INT_BITWISE.md. The 63-bit semantics are implemented correctly across all three paths and the test boundaries (1 61 shl,1 62 shl,-1 62 shl,-1 63 shl,-1 1 shr,-1 2 shr) all check out against the math. A few notes, none blocking.1. The 63-bit clamp logic is duplicated between
inline/ops.rsandspecialization/codegen_safe_math.rsemit_clamp_to_i63(crates/compiler/src/codegen/inline/ops.rs:280) ispub(in crate::codegen) fnonimpl CodeGen, andemit_specialized_safe_shift(crates/compiler/src/codegen/specialization/codegen_safe_math.rs:150) is alsoimpl CodeGen, so the helper is in scope. The latter inlines an identical 4-instructionfits_min/fits_max/and/selectsequence (lines 217–230 in the new file) instead of calling the helper. Same goes for the popcount mask, the clzsaturating-minus-1, and the ctzv==0 → 63shape — each is open-coded in bothinline/ops.rsandspecialization/codegen_ops.rs. A handful ofpub(in crate::codegen)helpers (emit_popcount_i63,emit_clz_i63,emit_ctz_i63) would collapse the duplication and remove the risk of the two paths drifting if the encoding ever changes.2. The 63-bit constants are repeated as magic numbers in IR-emitting code
-4611686018427387904,4611686018427387903, and9223372036854775807appear unannotated ininline/ops.rsandcodegen_safe_math.rs. The runtime gives them names (I63_MIN,I63_MAX,I63_MASKincrates/runtime/src/arithmetic/bitwise.rs:13–18); the codegen does not. If the tag scheme ever moves (the design doc treats 63-bit as load-bearing but not eternal), the IR strings are easy to miss in a search. A namedconst I63_MIN_LIT: &str = "-4611686018427387904"next to the helpers — or justformat!("{}", seqc::tagged::I63_MIN)— would tie all the call sites to one source of truth.3. Latent (pre-existing) overflow in the runtime shift count cast
crates/runtime/src/arithmetic/bitwise.rs:111and:138both doc as u32after thec < 0guard. For anyc > u32::MAX(allowed by the 63-bit Int range), the cast truncates the high bits, so e.g.1 4294967296 shlwould compute1.checked_shl(0) = 1instead of clamping to 0. The PR description notes this FFI is dead forshl/shr("onlyint_bitsactually goes through it"), so this is unobservable today — but if that ever changes, the contract documented at the top of the file ("counts >= 64 ... return 0") is silently violated. Cheap to fix here while the file is open:if c < 0 || c >= 64 { 0 } else { v.checked_shl(c as u32)... }.4. (Nit, observation only)
panic!catch-all in the inline intrinsic dispatchcrates/compiler/src/codegen/inline/ops.rs:402panics on an unknown intrinsic name. That's fine for an internal dispatch, but it does turn a previously totalif/elseinto something that can crash codegen if a fourth intrinsic is wired in upstream without updating this match. ACodeGenErrorwould degrade more gracefully; a comment pointing at the caller incodegen_inline_int_unary_intrinsic's sole call site would be enough if you'd rather keep the panic.Tests, docs, and the cross-path math all line up. Nice cleanup of the lying
int-bitsand the example in the language guide.