Add an Architecture Abstraction Layer.

Currently, we support one architecture, but this provides a layer for
adding other architectures without adding more nested `#ifdef`s.

Fixes #42
This commit is contained in:
David Chisnall
2019-07-10 10:42:59 +01:00
parent e594377b8a
commit 7eabea01d6
10 changed files with 214 additions and 115 deletions

86
src/aal/aal.h Normal file
View File

@@ -0,0 +1,86 @@
#pragma once
#include "ds/defines.h"
#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \
defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64)
# define PLATFORM_IS_X86
#endif
namespace snmalloc
{
/**
* Architecture Abstraction Layer. Includes default implementations of some
* functions using compiler builtins. Falls back to the definitions in the
* platform's AAL if the builtin does not exist.
*/
template<class Arch>
struct AAL_Generic : Arch
{
/**
* Prefetch a specific address.
*
* If the compiler provides a portable prefetch builtin, use it directly,
* otherwise delegate to the architecture-specific layer. This allows new
* architectures to avoid needing to implement a custom `prefetch` method
* if they are used only with a compiler that provides the builtin.
*/
static inline void prefetch(void* ptr)
{
#if __has_builtin(__builtin_prefetch) && !defined(SNMALLOC_NO_AAL_BUILTINS)
__builtin_prefetch(ptr);
#else
Arch::prefetch(ptr);
#endif
}
/**
* Return an architecture-specific cycle counter.
*
* If the compiler provides a portable prefetch builtin, use it directly,
* otherwise delegate to the architecture-specific layer. This allows new
* architectures to avoid needing to implement a custom `tick` method
* if they are used only with a compiler that provides the builtin.
*/
static inline uint64_t tick()
{
#if __has_builtin(__builtin_readcyclecounter) && \
!defined(SNMALLOC_NO_AAL_BUILTINS)
return __builtin_readcyclecounter();
#else
return Arch::tick();
#endif
}
};
}
#ifdef PLATFORM_IS_X86
# include "aal_x86.h"
#endif
namespace snmalloc
{
using AAL = AAL_Generic<AAL_Arch>;
}
#if defined(_MSC_VER) && defined(SNMALLOC_VA_BITS_32)
# include <intsafe.h>
#endif
#ifdef __POINTER_WIDTH__
# if ((__POINTER_WIDTH__ == 64) && !defined(SNMALLOC_VA_BITS_64)) || \
((__POINTER_WIDTH__ == 32) && !defined(SNMALLOC_VA_BITS_32))
# error Compiler and PAL define inconsistent bit widths
# endif
#endif
#if defined(SNMALLOC_VA_BITS_32) && defined(SNMALLOC_VA_BITS_64)
# error Only one of SNMALLOC_VA_BITS_64 and SNMALLOC_VA_BITS_32 may be defined!
#endif
#ifdef SNMALLOC_VA_BITS_32
static_assert(sizeof(size_t) == 4);
#elif defined(SNMALLOC_VA_BITS_64)
static_assert(sizeof(size_t) == 8);
#endif

112
src/aal/aal_x86.h Normal file
View File

@@ -0,0 +1,112 @@
#pragma once
#ifdef _MSC_VER
# include <immintrin.h>
# include <intrin.h>
#else
# include <cpuid.h>
# include <emmintrin.h>
#endif
#if defined(__linux__) && !defined(OPEN_ENCLAVE)
# include <x86intrin.h>
#endif
#if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64)
# define SNMALLOC_VA_BITS_64
#else
# define SNMALLOC_VA_BITS_32
#endif
namespace snmalloc
{
/**
* x86-specific architecture abstraction layer.
*/
class AAL_x86
{
/**
* Read the timestamp counter, guaranteeing that all previous instructions
* have been retired.
*/
static inline uint64_t tickp()
{
unsigned int aux;
#if defined(_MSC_VER)
return __rdtscp(&aux);
#else
return __builtin_ia32_rdtscp(&aux);
#endif
}
/**
* Issue a fully serialising instruction.
*/
static inline void halt_out_of_order()
{
#if defined(_MSC_VER)
int cpu_info[4];
__cpuid(cpu_info, 0);
#else
unsigned int eax, ebx, ecx, edx;
__get_cpuid(0, &eax, &ebx, &ecx, &edx);
#endif
}
public:
/**
* On pipelined processors, notify the core that we are in a spin loop and
* that speculative execution past this point may not be a performance gain.
*/
static inline void pause()
{
_mm_pause();
}
/**
* Issue a prefetch hint at the specified address.
*/
static inline void prefetch(void* ptr)
{
_mm_prefetch(reinterpret_cast<const char*>(ptr), _MM_HINT_T0);
}
/**
* Return a cycle counter value.
*/
static inline uint64_t tick()
{
#if defined(_MSC_VER)
return __rdtsc();
#else
return __builtin_ia32_rdtsc();
#endif
}
/**
* Return the cycle counter value that can be used to indicate the start of
* a benchmark run. This is responsible for ensuring any serialisation
* that the pipeline may require.
*/
static inline uint64_t benchmark_time_start()
{
halt_out_of_order();
return AAL_Generic<AAL_x86>::tick();
}
/**
* Return the cycle counter value that can be used to indicate the end of a
* benchmark run. This is responsible for ensuring any serialisation that
* the pipeline may require.
*/
static inline uint64_t benchmark_time_end()
{
uint64_t t = tickp();
halt_out_of_order();
return t;
}
};
using AAL_Arch = AAL_x86;
}