fixes #21564; std/bitops: Add explicit type masking for the JS target (#21598)

* std/bitops: Add explicit type masking for the JS target

Typecasts on the JavaScript backend do not function the same way as they
do on C and C++ backends, so for bitwise operations we may need to mask them
back down into their allowed range when they get shifted outside it.

Since they do work as expected on the other backends, a default bitmask
of all 1's is casted down into the target type as an easily optimizable
"& 0xFF" operation for these backends.

* Fixup: this should still be a func

* Run test case on js target

* Adapt testcase to contributor guide and best practices

* Simplify constrain logic and turn into actual no-op for the C side
This commit is contained in:
chmod222
2023-04-03 05:22:31 +02:00
committed by GitHub
parent 6ec9c7f683
commit 31d3606fe8
2 changed files with 37 additions and 3 deletions

View File

@@ -63,6 +63,12 @@ macro bitxor*[T: SomeInteger](x, y: T; z: varargs[T]): T =
type BitsRange*[T] = range[0..sizeof(T)*8-1]
## A range with all bit positions for type `T`.
template typeMasked[T: SomeInteger](x: T): T =
when defined(js):
x and ((0xffffffff_ffffffff'u shr (64 - sizeof(T) * 8)))
else:
x
func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
## Returns an extracted (and shifted) slice of bits from `v`.
runnableExamples:
@@ -73,7 +79,7 @@ func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1,
let
upmost = sizeof(T) * 8 - 1
uv = v.castToUnsigned
(uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T
((uv shl (upmost - slice.b)).typeMasked shr (upmost - slice.b + slice.a)).T
proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
## Mutates `v` into an extracted (and shifted) slice of bits from `v`.
@@ -85,7 +91,7 @@ proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1,
let
upmost = sizeof(T) * 8 - 1
uv = v.castToUnsigned
v = (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T
v = ((uv shl (upmost - slice.b)).typeMasked shr (upmost - slice.b + slice.a)).T
func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
## Creates a bitmask based on a slice of bits.
@@ -96,7 +102,7 @@ func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
let
upmost = sizeof(T) * 8 - 1
bitmask = bitnot(0.T).castToUnsigned
(bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T
((bitmask shl (upmost - slice.b + slice.a)).typeMasked shr (upmost - slice.b)).T
proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
## Returns `v`, with only the `1` bits from `mask` matching those of

28
tests/stdlib/t21564.nim Normal file
View File

@@ -0,0 +1,28 @@
discard """
targets: "c js"
"""
import bitops
import std/assertions
proc main() =
block: # bug #21564
# tesk `bitops.bitsliced` patch
doAssert(0x17.bitsliced(4..7) == 0x01)
doAssert(0x17.bitsliced(0..3) == 0x07)
block:
# test in-place `bitops.bitslice`
var t = 0x12F4
t.bitslice(4..7)
doAssert(t == 0xF)
block:
# test `bitops.toMask` patch via bitops.masked
doAssert(0x12FFFF34.masked(8..23) == 0x00FFFF00)
main()
static:
main()