Skip to content

Commit

Permalink
fix bswap for odd number of bytes
Browse files Browse the repository at this point in the history
Fixes #38.

`bswap` was seeming to work well since Julia v1.6, but LLVM documents that
`bswap` is valid only for an even number of bytes
(https://llvm.org/docs/LangRef.html#llvm-bswap-intrinsics),
and a julia build with LLVM asserts enabled catches that.

NB: to enable LLVM asserts, put the following in "Make.user" :
```
FORCE_ASSERTIONS=1
LLVM_ASSERTIONS=1
```
  • Loading branch information
rfourquet committed Oct 5, 2023
1 parent 24ed569 commit 2a1026a
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "BitIntegers"
uuid = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"
authors = ["Rafael Fourquet <fourquet.rafael@gmail.com>"]
version = "0.3.0"
version = "0.3.1"

[deps]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ recompiled (fixed by https://github.com/JuliaLang/julia/pull/30830);
to errors and segfaults (cf. e.g. https://github.com/rfourquet/BitIntegers.jl/issues/1, fixed by
https://github.com/JuliaLang/julia/pull/33283).


## Release notes

### v0.3.1

* fix incorrect `bswap` for odd byte-sizes ([#41](https://github.com/rfourquet/BitIntegers.jl/pull/41))

### v0.3.0

* change and document how `promote_rule` is implemented ([#36](https://github.com/rfourquet/BitIntegers.jl/pull/36))
Expand Down
16 changes: 14 additions & 2 deletions src/BitIntegers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,26 @@ leading_zeros( x::XBI) = Int(ctlz_int(x))
trailing_zeros(x::XBI) = Int(cttz_int(x))

function bswap(x::XBI)
if VERSION < v"1.6" && sizeof(x) % 2 != 0
if isodd(sizeof(x))
# llvm instruction is invalid
error("unimplemented")
bswap_simple(x)
else
bswap_int(x)
end
end

# llvm is clever enough to transform that into `bswap` of the input truncated to the correct
# size (8 bits less), followed by a "funnel shift left" `fshl`
function bswap_simple(x::XBI)
y = zero(x)
for _ = 1:sizeof(x)
y <<= 8
y |= x % UInt8
x >>>= 8
end
y
end

flipsign(x::T, y::T) where {T<:XBS} = flipsign_int(x, y)

# this doesn't catch flipsign(x::BBS, y::BBS), which is more specific in Base
Expand Down
18 changes: 14 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,25 @@ end
k, l = rand(Int(typemin(Int8)):-1, 2)
for X in XInts
for op in (~, bswap)
if VERSION < v"1.6" && sizeof(X) % 2 != 0 && op == bswap
@test_throws ErrorException op(X(i))
continue
end
x = op(X(i))
@test x isa X
@test x != X(i) # we assume sizeof(X) > 8
@test op(x) == X(i)
end
# bswap specific
if VERSION >= v"1.6"
for y = rand(X, 20)
x = bswap(y)
if iseven(sizeof(x))
# test that default implemented matches bswap_simple
@test x == BitIntegers.bswap_simple(y)
else
# test that default implemented (i.e. bswap_simple) matches bswap_odd
@test x == bswap_odd(y)
end
end
end

for op in (count_ones, leading_zeros, trailing_zeros, leading_ones, trailing_ones)
r = op(X(i))
@test r isa Int
Expand Down
25 changes: 25 additions & 0 deletions test/setup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,29 @@ const TypeCombos =
[((X, Y) for X in Ints for Y in (X BInts ? XInts : Ints))...,
(Int, Int), (Int, UInt), (UInt, Int), (UInt, UInt)]


# initial attempt at implementing bswap for odd byte-sizes, but it's less efficient
# than the `bswap_simple` version after being optimized by llvm!
# let's keep this version around to test values
if VERSION >= v"1.6"
@generated function bswap_odd(x::BitIntegers.XBI)
@assert isodd(sizeof(x))
nb = sizeof(x) * 8
ix = "i$nb"
iy = "i$(nb+8)"
quote
Base.llvmcall(($"""
declare $iy @llvm.bswap.$iy($iy %Val)
define $ix @entry($ix) {
%y = zext $ix %0 to $iy
%y2 = call $iy @llvm.bswap.$iy($iy %y)
%y3 = lshr $iy %y2, 8
%x2 = trunc $iy %y3 to $ix
ret $ix %x2
}
""", "entry"), $x, Tuple{$x}, x)
end
end
end

nothing

0 comments on commit 2a1026a

Please sign in to comment.