Showing with 151 additions and 89 deletions.
  1. +151 −89 src/core/bitop.d
240 changes: 151 additions & 89 deletions src/core/bitop.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Copyright: Copyright Don Clugston 2005 - 2013.
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Authors: Don Clugston, Sean Kelly, Walter Bright, Alex Rønne Petersen
* Authors: Don Clugston, Sean Kelly, Walter Bright, Alex Rønne Petersen, Thomas Stuart Bockman
* Source: $(DRUNTIMESRC core/_bitop.d)
*/

Expand Down Expand Up @@ -40,12 +40,27 @@ private union Split64
uint lo;
}
}

pragma(inline, true)
this(ulong u64) @safe pure nothrow @nogc
{
if (__ctfe)
{
lo = cast(uint) u64;
hi = cast(uint) (u64 >>> 32);
}
else
this.u64 = u64;
}
}

unittest
{
const s = Split64(1);
assert((s.lo == 1) && (s.hi == 0));
const rt = Split64(1);
assert((rt.lo == 1) && (rt.hi == 0));

enum ct = Split64(1);
assert((ct.lo == rt.lo) && (ct.hi == rt.hi));
}

/**
Expand Down Expand Up @@ -80,6 +95,12 @@ unittest
assert(bsf(ulong.max << 39) == 39);
}

unittest
{
// Make sure bsf() is available at CTFE
enum test_ctfe = bsf(ulong.max);
assert(test_ctfe == 0);
}

/**
* Scans the bits in v from the most significant bit
Expand Down Expand Up @@ -114,6 +135,13 @@ unittest
assert(bsr((ulong.max >> 15) - 1) == 48);
}

unittest
{
// Make sure bsr() is available at CTFE
enum test_ctfe = bsr(ulong.max);
assert(test_ctfe == 63);
}

/**
* Tests the bit.
* (No longer an intrisic - the compiler recognizes the patterns
Expand Down Expand Up @@ -216,6 +244,21 @@ int bts(size_t* p, size_t bitnum) pure @system;
*/
uint bswap(uint v) pure;

/**
* Swaps bytes in an 8 byte ulong end-to-end, i.e. byte 0 becomes
* byte 7, byte 1 becomes byte 6, etc.
*/
ulong bswap(ulong v) pure
{
auto sv = Split64(v);

const temp = sv.lo;
sv.lo = bswap(sv.hi);
sv.hi = bswap(temp);

return (cast(ulong) sv.hi << 32) | sv.lo;
}

version (DigitalMars) version (AnyX86) @system // not pure
{
/**
Expand Down Expand Up @@ -271,16 +314,13 @@ int popcnt(uint x) pure
{
static if (is(typeof(_popcnt(uint.max))))
{
if(!__ctfe)
{
import core.cpuid;
if (hasPopcnt)
return _popcnt(x);
}
import core.cpuid;
if (!__ctfe && hasPopcnt)
return _popcnt(x);
}
}

return soft_popcnt!uint(x);
return softPopcnt!uint(x);
}
}

Expand Down Expand Up @@ -313,43 +353,30 @@ int popcnt(ulong x) pure

static if (size_t.sizeof == uint.sizeof)
{
Split64 sx = void;
if(__ctfe)
{
// union repainting doesn't work in CTFE
sx.lo = cast(uint)x;
sx.hi = cast(uint)(x >> 32);
}
else
const sx = Split64(x);
version(DigitalMars)
{
sx.u64 = x;
version(DigitalMars)
static if (is(typeof(_popcnt(uint.max))))
{
static if (is(typeof(_popcnt(uint.max))))
{
if (hasPopcnt)
return _popcnt(sx.lo) + _popcnt(sx.hi);
}
if (!__ctfe && hasPopcnt)
return _popcnt(sx.lo) + _popcnt(sx.hi);
}
}

return soft_popcnt!uint(sx.lo) + soft_popcnt!uint(sx.hi);
return softPopcnt!uint(sx.lo) + softPopcnt!uint(sx.hi);
}
else static if (size_t.sizeof == ulong.sizeof)
{
version(DigitalMars)
{
static if (is(typeof(_popcnt(ulong.max))))
{
if(!__ctfe)
{
if (hasPopcnt)
return _popcnt(x);
}
if (!__ctfe && hasPopcnt)
return _popcnt(x);
}
}

return soft_popcnt!ulong(x);
return softPopcnt!ulong(x);
}
else
static assert(false);
Expand All @@ -369,7 +396,7 @@ unittest
assert(test_ctfe == 64);
}

private int soft_popcnt(N)(N x) pure
private int softPopcnt(N)(N x) pure
if (is(N == uint) || is(N == ulong))
{
// Avoid branches, and the potential for cache misses which
Expand Down Expand Up @@ -509,9 +536,96 @@ void volatileStore(ulong * ptr, ulong value); /// ditto
/**
* Reverses the order of bits in a 32-bit integer.
*/
@trusted uint bitswap( uint x ) pure
pragma(inline, true)
uint bitswap( uint x ) pure
{
if (!__ctfe)
{
static if (is(typeof(asmBitswap32(x))))
return asmBitswap32(x);
}

return softBitswap!uint(x);
}

unittest
{
version (AsmX86)
static void test(alias impl)()
{
assert (impl( 0x8000_0100 ) == 0x0080_0001);
foreach(i; 0 .. 32)
assert (impl(1 << i) == 1 << 32 - i - 1);
}

test!(bitswap)();
test!(softBitswap!uint)();
static if (is(typeof(asmBitswap32(0u))))
test!(asmBitswap32)();

// Make sure bitswap() is available at CTFE
enum test_ctfe = bitswap(1U);
assert(test_ctfe == (1U << 31));
}

/**
* Reverses the order of bits in a 64-bit integer.
*/
pragma(inline, true)
ulong bitswap ( ulong x ) pure
{
if (!__ctfe)
{
static if (is(typeof(asmBitswap64(x))))
return asmBitswap64(x);
}

return softBitswap!ulong(x);
}

unittest
{
static void test(alias impl)()
{
assert (impl( 0b1000000000000000000000010000000000000000100000000000000000000001)
== 0b1000000000000000000000010000000000000000100000000000000000000001);
assert (impl( 0b1110000000000000000000010000000000000000100000000000000000000001)
== 0b1000000000000000000000010000000000000000100000000000000000000111);
foreach (i; 0 .. 64)
assert (impl(1UL << i) == 1UL << 64 - i - 1);
}

test!(bitswap)();
test!(softBitswap!ulong)();
static if (is(typeof(asmBitswap64(0uL))))
test!(asmBitswap64)();

// Make sure bitswap() is available at CTFE
enum test_ctfe = bitswap(1UL);
assert(test_ctfe == (1UL << 63));
}

private N softBitswap(N)(N x) pure
if (is(N == uint) || is(N == ulong))
{
// swap 1-bit pairs:
enum mask1 = cast(N) 0x5555_5555_5555_5555L;
x = ((x >> 1) & mask1) | ((x & mask1) << 1);
// swap 2-bit pairs:
enum mask2 = cast(N) 0x3333_3333_3333_3333L;
x = ((x >> 2) & mask2) | ((x & mask2) << 2);
// swap 4-bit pairs:
enum mask4 = cast(N) 0x0F0F_0F0F_0F0F_0F0FL;
x = ((x >> 4) & mask4) | ((x & mask4) << 4);

// reverse the order of all bytes:
x = bswap(x);

return x;
}

version (AsmX86)
{
private uint asmBitswap32(uint x) @trusted pure
{
asm pure nothrow @nogc { naked; }

Expand Down Expand Up @@ -548,37 +662,11 @@ void volatileStore(ulong * ptr, ulong value); /// ditto
ret;
}
}
else
{
// swap odd and even bits
x = ((x >> 1) & 0x5555_5555) | ((x & 0x5555_5555) << 1);
// swap consecutive pairs
x = ((x >> 2) & 0x3333_3333) | ((x & 0x3333_3333) << 2);
// swap nibbles
x = ((x >> 4) & 0x0F0F_0F0F) | ((x & 0x0F0F_0F0F) << 4);
// swap bytes
x = ((x >> 8) & 0x00FF_00FF) | ((x & 0x00FF_00FF) << 8);
// swap 2-byte long pairs
x = ( x >> 16 ) | ( x << 16);
return x;

}
}


unittest
version (D_InlineAsm_X86_64)
{
assert( bitswap( 0x8000_0100 ) == 0x0080_0001 );
foreach(i; 0 .. 32)
assert(bitswap(1 << i) == 1 << 32 - i - 1);
}

/**
* Reverses the order of bits in a 64-bit integer.
*/
ulong bitswap ( ulong x ) pure @trusted
{
version (D_InlineAsm_X86_64)
private ulong asmBitswap64(ulong x) @trusted pure
{
asm pure nothrow @nogc { naked; }

Expand Down Expand Up @@ -617,30 +705,4 @@ ulong bitswap ( ulong x ) pure @trusted
ret;
}
}
else
{
// swap odd and even bits
x = ((x >> 1) & 0x5555_5555_5555_5555L) | ((x & 0x5555_5555_5555_5555L) << 1);
// swap consecutive pairs
x = ((x >> 2) & 0x3333_3333_3333_3333L) | ((x & 0x3333_3333_3333_3333L) << 2);
// swap nibbles
x = ((x >> 4) & 0x0f0f_0f0f_0f0f_0f0fL) | ((x & 0x0f0f_0f0f_0f0f_0f0fL) << 4);
// swap bytes
x = ((x >> 8) & 0x00FF_00FF_00FF_00FFL) | ((x & 0x00FF_00FF_00FF_00FFL) << 8);
// swap shorts
x = ((x >> 16) & 0x0000_FFFF_0000_FFFFL) | ((x & 0x0000_FFFF_0000_FFFFL) << 16);
// swap ints
x = ( x >> 32 ) | ( x << 32);
return x;
}
}

unittest
{
assert (bitswap( 0b1000000000000000000000010000000000000000100000000000000000000001)
== 0b1000000000000000000000010000000000000000100000000000000000000001);
assert (bitswap( 0b1110000000000000000000010000000000000000100000000000000000000001)
== 0b1000000000000000000000010000000000000000100000000000000000000111);
foreach (i; 0 .. 64)
assert(bitswap(1UL << i) == 1UL << 64 - i - 1);
}