Commit 75826b62 authored by Etienne Renault's avatar Etienne Renault
Browse files

bricks: update excluding C++17 features

* spot/bricks/brick-assert,
spot/bricks/brick-bitlevel,
spot/bricks/brick-hashset,
spot/bricks/brick-shmem: Here.
parent 18a075db
......@@ -58,21 +58,21 @@
#define ASSERT_EQ_IDX(i, x, y) static_cast< decltype(i, x, y, void(0)) >(0)
#endif
/* you must #include <brick-string.h> to use ASSERT_UNREACHABLE_F */
/* you must #include <brick-string> to use ASSERT_UNREACHABLE_F */
#define UNREACHABLE_F(...) ::brick::_assert::assert_die_fn( \
BRICK_LOCWRAP( BRICK_LOCATION( brick::string::fmtf(__VA_ARGS__) ) ) )
#define UNREACHABLE(x) ::brick::_assert::assert_die_fn( \
BRICK_LOCWRAP( BRICK_LOCATION( x ) ) )
#define UNREACHABLE(...) ::brick::_assert::assert_die_fn( \
BRICK_LOCWRAP( BRICK_LOCATION( ::brick::_assert::fmt( __VA_ARGS__ ) ) ) )
#define UNREACHABLE_() ::brick::_assert::assert_die_fn( \
BRICK_LOCWRAP( BRICK_LOCATION( "an unreachable location" ) ) )
#define NOT_IMPLEMENTED() ::brick::_assert::assert_die_fn( \
BRICK_LOCWRAP( BRICK_LOCATION( "a missing implementation" ) ) )
#ifdef _MSC_VER
#define DIVINE_UNUSED
#define UNUSED
#define noexcept
#else
#define DIVINE_UNUSED __attribute__((unused))
#define UNUSED __attribute__((unused))
#endif
#ifndef BRICK_ASSERT_H
......@@ -139,7 +139,7 @@ struct AssertFailed : std::exception
(*this) << ":\n " << expected << " " << l.stmt;
}
const char *what() const noexcept override { return str.c_str(); }
const char *what() const noexcept { return str.c_str(); }
};
static inline void format( AssertFailed & ) {}
......@@ -196,9 +196,9 @@ inline void assert_die_fn( Location l )
} \
}
ASSERT_FN(eq, ==, !=)
ASSERT_FN(leq, <=, >)
ASSERT_FN(lt, <, >=)
ASSERT_FN(eq, ==, !=);
ASSERT_FN(leq, <=, >);
ASSERT_FN(lt, <, >=);
template< typename Location, typename X >
void assert_pred_fn( Location l, X x, bool p )
......@@ -221,6 +221,10 @@ void assert_neq_fn( Location l, X x, Y y )
throw f;
}
inline std::string fmt( const char *str ) { return str; }
inline std::string fmt( std::string str ) { return str; }
// another overload of fmt is exported by brick-string and it allows more complex formating
}
}
......
......@@ -28,7 +28,7 @@
#include <limits>
#ifdef __linux
#include <asm/byteorder.h>
#include <sys/types.h>
#include <byteswap.h>
#elif !defined LITTLE_ENDIAN // if defined _WIN32
#define BYTE_ORDER 1234
......@@ -55,6 +55,28 @@ constexpr inline T1 downalign( T1 v, T2 a ) {
return v - (v % T1(a));
}
template< typename F, typename T >
void maybe_bitcast( F f, T &t )
{
ASSERT( sizeof( T ) == sizeof( F ) );
memcpy( &t, &f, sizeof( T ) );
}
template< typename F, typename T >
void bitcast( F f, T &t )
{
//static_assert( sizeof( F ) == sizeof( T ) );
maybe_bitcast( f, t );
}
template< typename T, typename F >
T bitcast( F f )
{
T t;
bitcast( f, t );
return t;
}
namespace compiletime {
template< typename T >
......@@ -89,10 +111,7 @@ struct bvpair
constexpr bvpair( L l, H h = 0 ) : low( l ), high( h ) {}
constexpr bvpair() = default;
explicit constexpr operator bool() const { return low || high; }
#if ((__GNUC__ >= 4 && __GNUC_MINOR__ > 9) || (__clang_major__ == 3 && __clang_minor__ >= 6))
constexpr
#endif
bvpair operator<<( int s ) const
constexpr bvpair operator<<( int s ) const
{
int rem = 8 * sizeof( low ) - s;
int unshift = std::max( rem, 0 );
......@@ -100,10 +119,7 @@ struct bvpair
H carry = ( low & ~ones< L >( unshift ) ) >> unshift;
return bvpair( low << s, ( high << s ) | ( carry << shift ) );
}
#if ((__GNUC__ >= 4 && __GNUC_MINOR__ > 9) || (__clang_major__ == 3 && __clang_minor__ >= 6))
constexpr
#endif
bvpair operator>>( int s ) const
constexpr bvpair operator>>( int s ) const
{
int rem = 8 * sizeof( low ) - s;
int unshift = std::max( rem, 0 );
......@@ -133,22 +149,45 @@ template< int i > using bitvec = typename _bitvec< i >::T;
namespace {
uint32_t mixdown( uint64_t i ) /* due to Thomas Wang */
template< typename T >
union Cast
{
bitvec< sizeof( T ) * 8 > r;
T c;
Cast( T c ) : c( c ) {}
Cast( bitvec< sizeof( T ) * 8 > r ) : r( r ) {}
};
template< typename T >
bitvec< sizeof( T ) * 8 > raw( T t )
{
i = (~i) + (i << 18);
i = i ^ (i >> 31);
i = i * 21;
i = i ^ (i >> 11);
i = i + (i << 6);
i = i ^ (i >> 22);
return i;
Cast< T > c( t );
return c.r;
}
__attribute__((unused)) uint32_t mixdown( uint32_t a, uint32_t b )
template< typename T >
T unraw( bitvec< sizeof( T ) * 8 > r )
{
return mixdown( ( uint64_t( a ) << 32 ) | uint64_t( b ) );
Cast< T > c( r );
return c.c;
}
// uint32_t mixdown( uint64_t i ) /* due to Thomas Wang */
// {
// i = (~i) + (i << 18);
// i = i ^ (i >> 31);
// i = i * 21;
// i = i ^ (i >> 11);
// i = i + (i << 6);
// i = i ^ (i >> 22);
// return i;
// }
// uint32_t mixdown( uint32_t a, uint32_t b )
// {
// return mixdown( ( uint64_t( a ) << 32 ) | uint64_t( b ) );
// }
}
/*
......@@ -367,13 +406,13 @@ struct BitField
return *this; \
}
OP(+=)
OP(-=)
OP(*=)
OP(/=)
OP(%=)
OP(|=)
OP(&=)
OP(+=);
OP(-=);
OP(*=);
OP(/=);
OP(%=);
OP(|=);
OP(&=);
#undef OP
};
};
......@@ -654,7 +693,8 @@ struct BitTupleTest {
ASSERT( !get< 0 >( bt ).word() );
}
TEST(assign) {
TEST(assign) /* TODO fails in DIVINE */
{
bitlevel::BitTuple<
BitField< bool, 1 >,
BitField< int, 6 >,
......
......@@ -43,7 +43,7 @@ using hash::hash128_t;
struct DefaultHasher
{
auto hash( int64_t v ) const -> std::pair<int64_t, int64_t>
auto hash( int64_t v ) const
{
return std::make_pair( v, ~v );
}
......@@ -153,7 +153,8 @@ struct FastAtomicCell : CellBase< T, Hasher >
return true;
}
bool tryStore( T v, hash32_t hash ) {
bool tryStore( T v, hash32_t hash )
{
hash |= 0x1;
hash32_t chl = 0;
if ( _hashlock.compare_exchange_strong( chl, (hash << 2) | 1 ) ) {
......@@ -164,6 +165,8 @@ struct FastAtomicCell : CellBase< T, Hasher >
return false;
}
void store( T v, hash32_t hash ) { tryStore( v, hash ); }
template< typename Value >
bool is( const Value &v, hash64_t hash, Hasher &h ) {
hash |= 0x1;
......@@ -209,9 +212,9 @@ struct AtomicCell : CellBase< T, Hasher >
{
std::atomic< Tagged< T > > value;
static_assert( sizeof( std::atomic< Tagged< T > > ) == sizeof( Tagged< T > ),
"std::atomic< Tagged< T > > must be lock-free" );
static_assert( Tagged< T >::tag_bits > 0, "T has at least a one-bit tagspace" );
// static_assert( sizeof( std::atomic< Tagged< T > > ) == sizeof( Tagged< T > ),
// "std::atomic< Tagged< T > > must be lock-free" );
// static_assert( Tagged< T >::tag_bits > 0, "T has at least a one-bit tagspace" );
bool empty() { return !value.load().t; }
bool invalid() {
......@@ -226,20 +229,19 @@ struct AtomicCell : CellBase< T, Hasher >
return AtomicCell( value.exchange( v ) );
}
Tagged< T > &deatomize() {
value.load(); // fence
return *reinterpret_cast< Tagged< T > * >( &value );
}
T &fetch() { return deatomize().t; }
T fetch() { return value.load().t; }
T copy() { Tagged< T > v = value; v.tag( 0 ); return v.t; }
bool wait() { return !invalid(); }
void store( T bn, hash64_t hash ) {
return tryStore( bn, hash );
void store( T bn, hash64_t hash )
{
Tagged< T > next( bn );
next.tag( highbits( hash, Tagged< T >::tag_bits ) | 1 );
value.store( next );
}
bool tryStore( T b, hash64_t hash ) {
bool tryStore( T b, hash64_t hash )
{
Tagged< T > zero;
Tagged< T > next( b );
next.tag( highbits( hash, Tagged< T >::tag_bits ) | 1 );
......@@ -299,13 +301,25 @@ struct HashSetBase
Hasher hasher;
struct iterator {
struct proxy
{
value_type v;
value_type *operator->() { return &v; }
};
struct iterator
{
Cell *_cell;
value_type _value;
bool _new;
iterator( Cell *c = nullptr, bool n = false ) : _cell( c ), _new( n ) {}
value_type *operator->() { return &(_cell->fetch()); }
value_type &operator*() { return _cell->fetch(); }
iterator( Cell *c = nullptr, bool n = false )
: _cell( c ), _value( c ? c->fetch() : value_type() ), _new( n )
{}
proxy operator->() { return proxy( _value ); }
value_type operator*() { return _value; }
value_type copy() { return _cell->copy(); }
void update( value_type v ) { _cell->store( v ); }
bool valid() { return _cell; }
bool isnew() { return _new; }
};
......@@ -355,13 +369,16 @@ struct _HashSet : HashSetBase< Cell >
size_t _maxsize;
bool _growing;
size_t size() const { return _table.size(); }
size_t capacity() const { return _table.size(); }
bool empty() const { return !_used; }
int count( const value_type &i ) { return find( i ).valid(); }
hash64_t hash( const value_type &i ) { return hash128( i ).first; }
hash128_t hash128( const value_type &i ) { return this->hasher.hash( i ); }
iterator insert( value_type i ) { return insertHinted( i, hash( i ) ); }
iterator insert( value_type i, bool replace = false )
{
return insertHinted( i, hash( i ), replace );
}
template< typename T >
iterator find( const T &i ) {
......@@ -387,27 +404,35 @@ struct _HashSet : HashSetBase< Cell >
return this->end();
}
iterator insertHinted( const value_type &i, hash64_t h ) {
return insertHinted( i, h, _table, _used );
iterator insertHinted( const value_type &i, hash64_t h, bool replace = false )
{
return insertHinted( i, h, _table, _used, replace );
}
iterator insertHinted( const value_type &item, hash64_t h, Table &table, int &used )
iterator insertHinted( const value_type &item, hash64_t h, Table &table, int &used,
bool replace = false )
{
if ( !_growing && size_t( _used ) > (size() / 100) * 75 )
if ( !_growing && size_t( _used ) > ( capacity() / 100 ) * 75 )
grow();
size_t idx;
for ( size_t i = 0; i < this->maxcollisions; ++i ) {
for ( size_t i = 0; i < this->maxcollisions; ++i )
{
idx = this->index( h, i, _bits );
if ( table[ idx ].empty() ) {
if ( table[ idx ].empty() )
{
++ used;
table[ idx ].store( item, h );
return iterator( &table[ idx ], true );
}
if ( table[ idx ].is( item, h, this->hasher ) )
{
if ( replace )
table[ idx ].store( item, h );
return iterator( &table[ idx ], false );
}
}
grow();
......@@ -416,7 +441,7 @@ struct _HashSet : HashSetBase< Cell >
}
void grow() {
if ( 2 * size() >= _maxsize )
if ( 2 * capacity() >= _maxsize )
UNREACHABLE( "ran out of space in the hash table" );
if( _growing )
......@@ -428,7 +453,7 @@ struct _HashSet : HashSetBase< Cell >
Table table;
table.resize( 2 * size(), Cell() );
table.resize( 2 * capacity(), Cell() );
_bits |= (_bits << 1); // unmask more
for ( auto cell : _table ) {
......@@ -444,7 +469,7 @@ struct _HashSet : HashSetBase< Cell >
_growing = false;
}
void setSize( size_t s )
void reserve( size_t s )
{
_bits = 0;
while ((s = s >> 1))
......@@ -454,7 +479,7 @@ struct _HashSet : HashSetBase< Cell >
void clear() {
_used = 0;
std::fill( _table.begin(), _table.end(), value_type() );
std::fill( _table.begin(), _table.end(), Cell() );
}
Cell &cellAt( size_t idx ) { return _table[ idx ]; }
......@@ -467,7 +492,7 @@ struct _HashSet : HashSetBase< Cell >
_HashSet( Hasher h, int initial )
: Base( h ), _used( 0 ), _maxsize( -1 ), _growing( false )
{
setSize( initial );
reserve( initial );
}
};
......@@ -595,28 +620,29 @@ struct _ConcurrentHashSet : HashSetBase< Cell >
return s * 2;
}
size_t size() { return current().size(); }
size_t capacity() { return current().size(); }
Row &current() { return _s->table[ _s->currentRow ]; }
Row &current( unsigned index ) { return _s->table[ index ]; }
bool changed( unsigned row ) { return row < _s->currentRow || _s->growing; }
iterator insert( value_type x )
iterator insert( value_type x, bool replace = false )
{
return insertHinted( x, hasher.hash( x ).first );
return insertHinted( x, hasher.hash( x ).first, replace );
}
template< typename T >
iterator find( const T &x ) {
iterator find( const T &x )
{
return findHinted( x, hasher.hash( x ).first );
}
int count( value_type x ) { return find( x ).valid() ? 1 : 0; }
iterator insertHinted( value_type x, hash64_t h )
iterator insertHinted( value_type x, hash64_t h, bool replace = false )
{
while ( true ) {
Row &row = current( _l.currentRow );
Insert ir = insertCell< false >( row, x, h );
Insert ir = insertCell< false >( row, x, h, replace );
switch ( ir.r ) {
case Resolution::Success:
increaseUsage();
......@@ -689,7 +715,7 @@ struct _ConcurrentHashSet : HashSetBase< Cell >
}
template< bool force >
Insert insertCell( Row &row, value_type x, hash64_t h )
Insert insertCell( Row &row, value_type x, hash64_t h, bool replace = false )
{
if ( !force ) {
// read usage first to guarantee usage <= size
......@@ -709,14 +735,20 @@ struct _ConcurrentHashSet : HashSetBase< Cell >
{
Cell &cell = row[ Base::index( h, i, mask ) ];
if ( cell.empty() ) {
if ( cell.empty() )
{
if ( cell.tryStore( x, h ) )
return Insert( Resolution::Success, &cell );
if ( !force && changed( _l.currentRow ) )
return Insert( Resolution::Growing );
}
if ( cell.is( x, h, hasher ) )
{
if ( replace )
cell.store( x, h );
return Insert( Resolution::Found, &cell );
}
if ( !force && changed( _l.currentRow ) )
return Insert( Resolution::Growing );
......@@ -932,7 +964,7 @@ struct Sequential
ASSERT( set.count( 1 ) );
unsigned count = 0;
for ( unsigned i = 0; i != set.size(); ++i )
for ( unsigned i = 0; i != set.capacity(); ++i )
if ( set.valueAt( i ) )
++count;
......@@ -1024,7 +1056,7 @@ struct Parallel
static HS< int > _multi( std::size_t count, int from, int to )
{
shmem::ThreadSet< Insert > arr;
arr.resize( count, Insert() );
arr.resize( count );
arr[ 0 ].set.initialSize( isize );
for ( std::size_t i = 0; i < count; ++i )
......@@ -1049,7 +1081,7 @@ struct Parallel
int count = 0;
std::set< int > s;
for ( size_t i = 0; i != set.size(); ++i ) {
for ( size_t i = 0; i != set.capacity(); ++i ) {
if ( set.valueAt( i ) ) {
if ( s.find( set.valueAt( i ) ) == s.end() )
s.insert( set.valueAt( i ) );
......@@ -1395,7 +1427,7 @@ struct RandomInsert {
: insert( true ), max( max )
{
if ( bg->reserve() > 0 )
t.setSize( bg->items() * bg->reserve() );
t.reserve( bg->items() * bg->reserve() );
}
template< typename BG >
......@@ -1478,7 +1510,7 @@ struct wrap_set {
C< T > *t;
struct ThreadData {};
HashTable< T > withTD( ThreadData & ) { return *this; }
void setSize( int s ) { t->rehash( s ); }
void reserve( int s ) { t->rehash( s ); }
void insert( T i ) { t->insert( i ); }
int count( T i ) { return t->count( i ); }
HashTable() : t( new C< T > ) {}
......
......@@ -42,10 +42,10 @@
#include <iostream>
#include <typeinfo>
#include <mutex>
#include <atomic>
#include <thread>
#include <mutex>
#include <future>
#include <unistd.h> // alarm
#include <vector>
......@@ -54,18 +54,21 @@
#define BRICK_SHMEM_H
#ifndef BRICKS_CACHELINE
#if (__GNUC__ >= 7) // Please new Versions of GCC
#define BRICKS_CACHELINE 16
#else
#define BRICKS_CACHELINE 64
#endif
#endif
namespace brick {
namespace shmem {
struct ThreadBase
{
virtual void start() = 0;
virtual void stop() = 0;
virtual ~ThreadBase() {}
};
template< typename T >
struct Thread : T
struct Thread : T, ThreadBase
{
std::unique_ptr< std::thread > _thread;
bool _start_on_move; // :-(
......@@ -89,9 +92,9 @@ struct Thread : T
start();
}
virtual void start() noexcept
virtual void start()
{
_thread.reset( new std::thread( [this]() noexcept { this->main(); } ) );
_thread.reset( new std::thread( [this]() { this->main(); } ) );
}
virtual void stop()
......@@ -117,13 +120,6 @@ struct Thread : T
_thread.reset();
}
}
const Thread& operator=(const Thread& other)
{
std::cerr << "FIXME Added by us (Spot) to avoid compilation warnings\n";
std::cerr << " Should not pass here.\n";
return other;
}
};
template< typename T >
......@@ -178,13 +174,13 @@ struct AsyncLoop : Thread< LoopWrapper< T > >
stop(); /* call the correct stop(), with interrupt() */
}
void start() noexcept override
void start()
{
this->_interrupted.store( false, std::memory_order_relaxed );
Super::start();
}
void stop() override
void stop()
{
this->interrupt();
Super::stop();
......@@ -192,19 +188,22 @@ struct AsyncLoop : Thread< LoopWrapper< T > >
};
template< typename L >
auto async_loop( L &&l ) -> AsyncLoop< LambdaWrapper< L > >&&
using AsyncLambdaLoop = AsyncLoop< LambdaWrapper< L > >;
template< typename L >
auto async_loop( L &&l )
{
AsyncLoop< LambdaWrapper< L > > al( std::forward< L >( l ) );
AsyncLambdaLoop< L > al( std::forward< L >( l ) );
al._start_on_move = true;
return std::move( al );
}
template< typename L >
auto thread( L &&l ) -> Thread< LambdaWrapper< L > >
auto thread( L &&l )
{
Thread< LambdaWrapper< L > > thr( std::forward< L >( l ) );
thr._start_on_move = true;
return thr;
return std::move( thr );
}
template< typename T >
......@@ -213,7 +212,7 @@ struct ThreadSet : std::vector< Thread< T > >