1252 lines
30 KiB
C
1252 lines
30 KiB
C
/*********************************************************
|
|
* Copyright (C) 2003-2020 VMware, Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation version 2 and no later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*********************************************************/
|
|
|
|
/*
|
|
* vm_basic_asm.h
|
|
*
|
|
* Basic asm macros
|
|
*/
|
|
|
|
#ifndef _VM_BASIC_ASM_H_
|
|
#define _VM_BASIC_ASM_H_
|
|
|
|
#define INCLUDE_ALLOW_USERLEVEL
|
|
|
|
#define INCLUDE_ALLOW_MODULE
|
|
#define INCLUDE_ALLOW_VMMON
|
|
#define INCLUDE_ALLOW_VMK_MODULE
|
|
#define INCLUDE_ALLOW_VMKERNEL
|
|
#define INCLUDE_ALLOW_DISTRIBUTE
|
|
#define INCLUDE_ALLOW_VMCORE
|
|
#include "includeCheck.h"
|
|
|
|
#if defined _MSC_VER && !defined BORA_NO_WIN32_INTRINS
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4255) // disable no-prototype() to (void) warning
|
|
#include <intrin.h>
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#include "vm_basic_types.h"
|
|
|
|
#if defined VM_X86_64
|
|
#include "vm_basic_asm_x86_common.h"
|
|
#include "vm_basic_asm_x86_64.h"
|
|
#elif defined VM_X86_32
|
|
#include "vm_basic_asm_x86_common.h"
|
|
#include "vm_basic_asm_x86.h"
|
|
#elif defined VM_ARM_32
|
|
#include "vm_basic_asm_arm32.h"
|
|
#define MUL64_NO_ASM 1
|
|
#include "mul64.h"
|
|
#elif defined VM_ARM_64
|
|
#include "vm_basic_asm_arm64.h"
|
|
#else
|
|
#define MUL64_NO_ASM 1
|
|
#include "mul64.h"
|
|
#endif
|
|
|
|
#if defined __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Locate most and least significant bit set functions. Use our own name
|
|
* space to avoid namespace collisions. The new names follow a pattern,
|
|
* <prefix><size><option>, where:
|
|
*
|
|
* <prefix> is [lm]ssb (least/most significant bit set)
|
|
* <size> is size of the argument: 32 (32-bit), 64 (64-bit) or Ptr (pointer)
|
|
* <option> is for alternative versions of the functions
|
|
*
|
|
* NAME FUNCTION BITS FUNC(0)
|
|
*----- -------- ---- -------
|
|
* lssb32_0 LSB set (uint32) 0..31 -1
|
|
* mssb32_0 MSB set (uint32) 0..31 -1
|
|
* lssb64_0 LSB set (uint64) 0..63 -1
|
|
* mssb64_0 MSB set (uint64) 0..63 -1
|
|
* lssbPtr_0 LSB set (uintptr_t;32-bit) 0..31 -1
|
|
* lssbPtr_0 LSB set (uintptr_t;64-bit) 0..63 -1
|
|
* mssbPtr_0 MSB set (uintptr_t;32-bit) 0..31 -1
|
|
* mssbPtr_0 MSB set (uintptr_t;64-bit) 0..63 -1
|
|
* lssbPtr LSB set (uintptr_t;32-bit) 1..32 0
|
|
* lssbPtr LSB set (uintptr_t;64-bit) 1..64 0
|
|
* mssbPtr MSB set (uintptr_t;32-bit) 1..32 0
|
|
* mssbPtr MSB set (uintptr_t;64-bit) 1..64 0
|
|
* lssb32 LSB set (uint32) 1..32 0
|
|
* mssb32 MSB set (uint32) 1..32 0
|
|
* lssb64 LSB set (uint64) 1..64 0
|
|
* mssb64 MSB set (uint64) 1..64 0
|
|
*/
|
|
|
|
#ifdef _MSC_VER
|
|
static INLINE int
|
|
lssb32_0(const uint32 value)
|
|
{
|
|
unsigned long idx;
|
|
unsigned char ret;
|
|
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
}
|
|
ret = _BitScanForward(&idx, (unsigned long)value);
|
|
#ifdef __analysis_assume
|
|
__analysis_assume(ret != 0);
|
|
#endif
|
|
|
|
#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
|
|
return idx;
|
|
}
|
|
|
|
static INLINE int
|
|
mssb32_0(const uint32 value)
|
|
{
|
|
unsigned long idx;
|
|
unsigned char ret;
|
|
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
}
|
|
ret = _BitScanReverse(&idx, (unsigned long)value);
|
|
#ifdef __analysis_assume
|
|
__analysis_assume(ret != 0);
|
|
#endif
|
|
|
|
#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
|
|
return idx;
|
|
}
|
|
|
|
static INLINE int
|
|
lssb64_0(const uint64 value)
|
|
{
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
#ifdef VM_X86_64
|
|
unsigned long idx;
|
|
unsigned char ret;
|
|
|
|
ret = _BitScanForward64(&idx, (unsigned __int64)value);
|
|
#ifdef __analysis_assume
|
|
__analysis_assume(ret != 0);
|
|
#endif
|
|
|
|
#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
|
|
return idx;
|
|
#else
|
|
/* The coding was chosen to minimize conditionals and operations */
|
|
int lowFirstBit = lssb32_0((uint32) value);
|
|
if (lowFirstBit == -1) {
|
|
lowFirstBit = lssb32_0((uint32) (value >> 32));
|
|
if (lowFirstBit != -1) {
|
|
return lowFirstBit + 32;
|
|
}
|
|
}
|
|
return lowFirstBit;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static INLINE int
|
|
mssb64_0(const uint64 value)
|
|
{
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
#ifdef VM_X86_64
|
|
unsigned long idx;
|
|
unsigned char ret;
|
|
|
|
ret = _BitScanReverse64(&idx, (unsigned __int64)value);
|
|
#ifdef __analysis_assume
|
|
__analysis_assume(ret != 0);
|
|
#endif
|
|
|
|
#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
|
|
return idx;
|
|
#else
|
|
/* The coding was chosen to minimize conditionals and operations */
|
|
if (value > 0xFFFFFFFFULL) {
|
|
return 32 + mssb32_0((uint32) (value >> 32));
|
|
}
|
|
return mssb32_0((uint32) value);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
|
|
#ifdef VM_X86_ANY
|
|
#define USE_ARCH_X86_CUSTOM
|
|
#endif
|
|
|
|
/* **********************************************************
|
|
* GCC's intrinsics for the lssb and mssb family produce sub-optimal code,
|
|
* so we use inline assembly to improve matters. However, GCC cannot
|
|
* propagate constants through inline assembly, so we help GCC out by
|
|
* allowing it to use its intrinsics for compile-time constant values.
|
|
* Some day, GCC will make better code and these can collapse to intrinsics.
|
|
*
|
|
* For example, in Decoder_AddressSize, inlined into VVT_GetVTInstrInfo:
|
|
* __builtin_ffs(a) compiles to:
|
|
* mov $0xffffffff, %esi
|
|
* bsf %eax, %eax
|
|
* cmovz %esi, %eax
|
|
* sub $0x1, %eax
|
|
* and $0x7, %eax
|
|
*
|
|
* While the code below compiles to:
|
|
* bsf %eax, %eax
|
|
* sub $0x1, %eax
|
|
*
|
|
* Ideally, GCC should have recognized non-zero input in the first case.
|
|
* Other instances of the intrinsic produce code like
|
|
* sub $1, %eax; add $1, %eax; clts
|
|
* **********************************************************
|
|
*/
|
|
|
|
#if __GNUC__ < 4
|
|
#define FEWER_BUILTINS
|
|
#endif
|
|
|
|
static INLINE int
|
|
lssb32_0(uint32 v)
|
|
{
|
|
int value = (int)v;
|
|
#ifdef USE_ARCH_X86_CUSTOM
|
|
if (!__builtin_constant_p(value)) {
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
int pos;
|
|
__asm__ ("bsfl %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
|
|
return pos;
|
|
}
|
|
}
|
|
#endif
|
|
return __builtin_ffs(value) - 1;
|
|
}
|
|
|
|
#ifndef FEWER_BUILTINS
|
|
static INLINE int
|
|
mssb32_0(uint32 value)
|
|
{
|
|
/*
|
|
* We must keep the UNLIKELY(...) outside the #if defined ...
|
|
* because __builtin_clz(0) is undefined according to gcc's
|
|
* documentation.
|
|
*/
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
int pos;
|
|
#ifdef USE_ARCH_X86_CUSTOM
|
|
if (!__builtin_constant_p(value)) {
|
|
__asm__ ("bsrl %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
|
|
return pos;
|
|
}
|
|
#endif
|
|
pos = 32 - __builtin_clz(value) - 1;
|
|
return pos;
|
|
}
|
|
}
|
|
|
|
static INLINE int
|
|
lssb64_0(const uint64 v)
|
|
{
|
|
int64 value = (int64)v;
|
|
|
|
#ifdef USE_ARCH_X86_CUSTOM
|
|
if (!__builtin_constant_p(value)) {
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
intptr_t pos;
|
|
#ifdef VM_X86_64
|
|
__asm__ ("bsf %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
|
|
#else
|
|
/* The coding was chosen to minimize conditionals and operations */
|
|
pos = lssb32_0((uint32) value);
|
|
if (pos == -1) {
|
|
pos = lssb32_0((uint32) (value >> 32));
|
|
if (pos != -1) {
|
|
return pos + 32;
|
|
}
|
|
}
|
|
#endif
|
|
return pos;
|
|
}
|
|
}
|
|
#endif
|
|
return __builtin_ffsll(value) - 1;
|
|
}
|
|
#endif /* !FEWER_BUILTINS */
|
|
|
|
#ifdef FEWER_BUILTINS
|
|
/* GCC 3.3.x does not like __bulitin_clz or __builtin_ffsll. */
|
|
static INLINE int
|
|
mssb32_0(uint32 value)
|
|
{
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
int pos;
|
|
__asm__ __volatile__("bsrl %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
|
|
return pos;
|
|
}
|
|
}
|
|
|
|
static INLINE int
|
|
lssb64_0(const uint64 value)
|
|
{
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
intptr_t pos;
|
|
|
|
#ifdef VM_X86_64
|
|
__asm__ __volatile__("bsf %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
|
|
#else
|
|
/* The coding was chosen to minimize conditionals and operations */
|
|
pos = lssb32_0((uint32) value);
|
|
if (pos == -1) {
|
|
pos = lssb32_0((uint32) (value >> 32));
|
|
if (pos != -1) {
|
|
return pos + 32;
|
|
}
|
|
}
|
|
#endif /* VM_X86_64 */
|
|
return pos;
|
|
}
|
|
}
|
|
#endif /* FEWER_BUILTINS */
|
|
|
|
|
|
static INLINE int
|
|
mssb64_0(const uint64 value)
|
|
{
|
|
if (UNLIKELY(value == 0)) {
|
|
return -1;
|
|
} else {
|
|
intptr_t pos;
|
|
|
|
#ifdef USE_ARCH_X86_CUSTOM
|
|
#ifdef VM_X86_64
|
|
__asm__ ("bsr %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
|
|
#else
|
|
/* The coding was chosen to minimize conditionals and operations */
|
|
if (value > 0xFFFFFFFFULL) {
|
|
pos = 32 + mssb32_0((uint32) (value >> 32));
|
|
} else {
|
|
pos = mssb32_0((uint32) value);
|
|
}
|
|
#endif
|
|
#else
|
|
pos = 64 - __builtin_clzll(value) - 1;
|
|
#endif
|
|
|
|
return pos;
|
|
}
|
|
}
|
|
|
|
#ifdef USE_ARCH_X86_CUSTOM
|
|
#undef USE_ARCH_X86_CUSTOM
|
|
#endif
|
|
|
|
#endif // __GNUC__
|
|
|
|
static INLINE int
|
|
lssbPtr_0(const uintptr_t value)
|
|
{
|
|
#ifdef VM_64BIT
|
|
return lssb64_0((uint64) value);
|
|
#else
|
|
return lssb32_0((uint32) value);
|
|
#endif
|
|
}
|
|
|
|
static INLINE unsigned
|
|
lssbPtr(const uintptr_t value)
|
|
{
|
|
return (unsigned)lssbPtr_0(value) + 1;
|
|
}
|
|
|
|
static INLINE int
|
|
mssbPtr_0(const uintptr_t value)
|
|
{
|
|
#ifdef VM_64BIT
|
|
return mssb64_0((uint64) value);
|
|
#else
|
|
return mssb32_0((uint32) value);
|
|
#endif
|
|
}
|
|
|
|
static INLINE unsigned
|
|
mssbPtr(const uintptr_t value)
|
|
{
|
|
return (unsigned)mssbPtr_0(value) + 1;
|
|
}
|
|
|
|
static INLINE unsigned
|
|
lssb32(const uint32 value)
|
|
{
|
|
return (unsigned)lssb32_0(value) + 1;
|
|
}
|
|
|
|
static INLINE unsigned
|
|
mssb32(const uint32 value)
|
|
{
|
|
return (unsigned)mssb32_0(value) + 1;
|
|
}
|
|
|
|
static INLINE unsigned
|
|
lssb64(const uint64 value)
|
|
{
|
|
return (unsigned)lssb64_0(value) + 1;
|
|
}
|
|
|
|
static INLINE unsigned
|
|
mssb64(const uint64 value)
|
|
{
|
|
return (unsigned)mssb64_0(value) + 1;
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
#if defined(VM_X86_ANY) || defined(VM_ARM_ANY)
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* uint16set --
|
|
*
|
|
* memset a given address with an uint16 value, count times.
|
|
*
|
|
* Results:
|
|
* Pointer to filled memory range.
|
|
*
|
|
* Side effects:
|
|
* As with memset.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void *
|
|
uint16set(void *dst, uint16 val, size_t count)
|
|
{
|
|
#ifdef VM_ARM_32
|
|
void *tmpDst = dst;
|
|
|
|
__asm__ __volatile__ (
|
|
"cmp %1, #0\n\t"
|
|
"beq 2f\n"
|
|
"1:\n\t"
|
|
"strh %2, [%0], #2\n\t"
|
|
"subs %1, %1, #1\n\t"
|
|
"bne 1b\n"
|
|
"2:"
|
|
: "+r" (tmpDst), "+r" (count)
|
|
: "r" (val)
|
|
: "cc", "memory");
|
|
#elif defined(VM_ARM_64)
|
|
void *tmpDst = dst;
|
|
uint64 tmpVal = 0;
|
|
|
|
if (count == 0) {
|
|
return dst;
|
|
}
|
|
|
|
__asm__ __volatile__ (
|
|
"cbz %3, 1f\n\t"
|
|
|
|
// Copy 16 bits twice...
|
|
"bfm %2, %3, #0, #15\n\t"
|
|
"lsl %2, %2, #16\n\t"
|
|
"bfm %2, %3, #0, #15\n\t"
|
|
|
|
// Copy 32 bits from the bottom of the reg. to the top...
|
|
"lsl %2, %2, #32\n\t"
|
|
"bfm %2, %2, #32, #63\n"
|
|
|
|
// Copy into dst 8 bytes (4 uint16s) at a time
|
|
"1:\t"
|
|
"cmp %1, #4\n\t"
|
|
"b.lo 2f\n\t"
|
|
"str %2, [%0], #8\n\t"
|
|
"sub %1, %1, #4\n\t"
|
|
"b 1b\n"
|
|
|
|
// Copy into dst 4 bytes at a time
|
|
"2:\t"
|
|
"cmp %1, #2\n\t"
|
|
"b.lo 3f\n\t"
|
|
"str %w2, [%0], #4\n\t"
|
|
"sub %1, %1, #2\n\t"
|
|
"b 2b\n"
|
|
|
|
// We have 1 or zero items left...
|
|
"3:\t"
|
|
"cbz %1, 4f\n\t"
|
|
"strh %w2, [%0]\n"
|
|
"4:"
|
|
: "+r" (tmpDst), "+r" (count), "+r" (tmpVal)
|
|
: "r" ((uint64)val)
|
|
: "cc", "memory");
|
|
#else
|
|
size_t dummy0;
|
|
void *dummy1;
|
|
|
|
__asm__ __volatile__("\t"
|
|
"cld" "\n\t"
|
|
"rep ; stosw" "\n"
|
|
: "=c" (dummy0), "=D" (dummy1)
|
|
: "0" (count), "1" (dst), "a" (val)
|
|
: "memory", "cc"
|
|
);
|
|
#endif
|
|
return dst;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* uint32set --
|
|
*
|
|
* memset a given address with an uint32 value, count times.
|
|
*
|
|
* Results:
|
|
* Pointer to filled memory range.
|
|
*
|
|
* Side effects:
|
|
* As with memset.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void *
|
|
uint32set(void *dst, uint32 val, size_t count)
|
|
{
|
|
#ifdef VM_ARM_32
|
|
void *tmpDst = dst;
|
|
|
|
__asm__ __volatile__ (
|
|
"cmp %1, #0\n\t"
|
|
"beq 2f\n"
|
|
"1:\n\t"
|
|
"str %2, [%0], #4\n\t"
|
|
"subs %1, %1, #1\n\t"
|
|
"bne 1b\n"
|
|
"2:"
|
|
: "+r" (tmpDst), "+r" (count)
|
|
: "r" (val)
|
|
: "cc", "memory");
|
|
#elif defined(VM_ARM_64)
|
|
void *tmpDst = dst;
|
|
uint64 tmpVal = val;
|
|
|
|
if (count == 0) {
|
|
return dst;
|
|
}
|
|
|
|
__asm__ __volatile__ (
|
|
"cbz %2, 1f\n\t"
|
|
|
|
// Drop our value in the top 32 bits, then copy from there to the bottom
|
|
"lsl %2, %2, #32\n\t"
|
|
"bfm %2, %2, #32, #63\n"
|
|
|
|
// Copy four at a time
|
|
"1:\t"
|
|
"cmp %1, #16\n\t"
|
|
"b.lo 2f\n\t"
|
|
"stp %2, %2, [%0], #16\n\t"
|
|
"stp %2, %2, [%0], #16\n\t"
|
|
"stp %2, %2, [%0], #16\n\t"
|
|
"stp %2, %2, [%0], #16\n\t"
|
|
"sub %1, %1, #16\n\t"
|
|
"b 1b\n"
|
|
|
|
// Copy remaining pairs of data
|
|
"2:\t"
|
|
"cmp %1, #2\n\t"
|
|
"b.lo 3f\n\t"
|
|
"str %2, [%0], #8\n\t"
|
|
"sub %1, %1, #2\n\t"
|
|
"b 2b\n"
|
|
|
|
// One or zero values left to copy
|
|
"3:\t"
|
|
"cbz %1, 4f\n\t"
|
|
"str %w2, [%0]\n\t" // No incr
|
|
"4:"
|
|
: "+r" (tmpDst), "+r" (count), "+r" (tmpVal)
|
|
:
|
|
: "cc", "memory");
|
|
#else
|
|
size_t dummy0;
|
|
void *dummy1;
|
|
|
|
__asm__ __volatile__("\t"
|
|
"cld" "\n\t"
|
|
"rep ; stosl" "\n"
|
|
: "=c" (dummy0), "=D" (dummy1)
|
|
: "0" (count), "1" (dst), "a" (val)
|
|
: "memory", "cc"
|
|
);
|
|
#endif
|
|
return dst;
|
|
}
|
|
|
|
#else /* unknown system: rely on C to write */
|
|
static INLINE void *
|
|
uint16set(void *dst, uint16 val, size_t count)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < count; i++) {
|
|
((uint16 *) dst)[i] = val;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static INLINE void *
|
|
uint32set(void *dst, uint32 val, size_t count)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < count; i++) {
|
|
((uint32 *) dst)[i] = val;
|
|
}
|
|
return dst;
|
|
}
|
|
#endif // defined(VM_X86_ANY) || defined(VM_ARM_ANY)
|
|
#elif defined(_MSC_VER)
|
|
|
|
static INLINE void *
|
|
uint16set(void *dst, uint16 val, size_t count)
|
|
{
|
|
#ifdef VM_X86_ANY
|
|
__stosw((uint16*)dst, val, count);
|
|
#else
|
|
size_t i;
|
|
for (i = 0; i < count; i++) {
|
|
((uint16 *)dst)[i] = val;
|
|
}
|
|
#endif
|
|
return dst;
|
|
}
|
|
|
|
static INLINE void *
|
|
uint32set(void *dst, uint32 val, size_t count)
|
|
{
|
|
#ifdef VM_X86_ANY
|
|
__stosd((unsigned long*)dst, (unsigned long)val, count);
|
|
#else
|
|
size_t i;
|
|
for (i = 0; i < count; i++) {
|
|
((uint32 *)dst)[i] = val;
|
|
}
|
|
#endif
|
|
return dst;
|
|
}
|
|
|
|
#else
|
|
#error "No compiler defined for uint*set"
|
|
#endif
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Bswap16 --
|
|
*
|
|
* Swap the 2 bytes of "v" as follows: 32 -> 23.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE uint16
|
|
Bswap16(uint16 v)
|
|
{
|
|
#if defined(VM_ARM_64)
|
|
__asm__("rev16 %w0, %w0" : "+r"(v));
|
|
return v;
|
|
#else
|
|
return ((v >> 8) & 0x00ff) | ((v << 8) & 0xff00);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Bswap32 --
|
|
*
|
|
* Swap the 4 bytes of "v" as follows: 3210 -> 0123.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE uint32
|
|
Bswap32(uint32 v) // IN
|
|
{
|
|
#if defined(__GNUC__) && defined(VM_X86_ANY)
|
|
/* Checked against the Intel manual and GCC. --hpreg */
|
|
__asm__(
|
|
"bswap %0"
|
|
: "=r" (v)
|
|
: "0" (v)
|
|
);
|
|
return v;
|
|
#elif defined(VM_ARM_32) && !defined(__ANDROID__) && !defined(_MSC_VER)
|
|
__asm__("rev %0, %0" : "+r"(v));
|
|
return v;
|
|
#elif defined(VM_ARM_64)
|
|
__asm__("rev32 %x0, %x0" : "+r"(v));
|
|
return v;
|
|
#else
|
|
return (v >> 24)
|
|
| ((v >> 8) & 0xFF00)
|
|
| ((v & 0xFF00) << 8)
|
|
| (v << 24) ;
|
|
#endif
|
|
}
|
|
#define Bswap Bswap32
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Bswap64 --
|
|
*
|
|
* Swap the 8 bytes of "v" as follows: 76543210 -> 01234567.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE uint64
|
|
Bswap64(uint64 v) // IN
|
|
{
|
|
#if defined(VM_ARM_64)
|
|
__asm__("rev %0, %0" : "+r"(v));
|
|
return v;
|
|
#else
|
|
return ((uint64)Bswap((uint32)v) << 32) | Bswap((uint32)(v >> 32));
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* PAUSE is a P4 instruction that improves spinlock power+performance;
|
|
* on non-P4 IA32 systems, the encoding is interpreted as a REPZ-NOP.
|
|
* Use volatile to avoid NOP removal.
|
|
*/
|
|
static INLINE void
|
|
PAUSE(void)
|
|
#if defined(__GNUC__) || defined(VM_ARM_32)
|
|
{
|
|
#ifdef VM_ARM_64
|
|
__asm__ __volatile__("yield");
|
|
#elif defined VM_ARM_32
|
|
/*
|
|
* YIELD is available in ARMv6K and above, so we could probably refine this
|
|
* instead of leaving it empty.
|
|
*/
|
|
#else // x86
|
|
__asm__ __volatile__( "pause" :);
|
|
#endif
|
|
}
|
|
#elif defined(_MSC_VER)
|
|
{
|
|
_mm_pause();
|
|
}
|
|
#else /* __GNUC__ */
|
|
#error No compiler defined for PAUSE
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Checked against the Intel manual and GCC --hpreg
|
|
*
|
|
* volatile because the tsc always changes without the compiler knowing it.
|
|
*/
|
|
static INLINE uint64
|
|
RDTSC(void)
|
|
#ifdef __GNUC__
|
|
{
|
|
#ifdef VM_X86_64
|
|
uint64 tscLow;
|
|
uint64 tscHigh;
|
|
|
|
__asm__ __volatile__(
|
|
"rdtsc"
|
|
: "=a" (tscLow), "=d" (tscHigh)
|
|
);
|
|
|
|
return tscHigh << 32 | tscLow;
|
|
#elif defined(VM_X86_32)
|
|
uint64 tim;
|
|
|
|
__asm__ __volatile__(
|
|
"rdtsc"
|
|
: "=A" (tim)
|
|
);
|
|
|
|
return tim;
|
|
#elif defined(VM_ARM_64)
|
|
#if (defined(VMKERNEL) || defined(VMM)) && !defined(VMK_ARM_EL1)
|
|
return MRS(CNTPCT_EL0);
|
|
#else
|
|
return MRS(CNTVCT_EL0);
|
|
#endif
|
|
#else
|
|
/*
|
|
* For platform without cheap timer, just return 0.
|
|
*/
|
|
return 0;
|
|
#endif
|
|
}
|
|
#elif defined(_MSC_VER)
|
|
#ifdef VM_X86_ANY
|
|
{
|
|
return __rdtsc();
|
|
}
|
|
#else
|
|
{
|
|
/*
|
|
* We need to do more inverstagetion here to find
|
|
* a microsoft equivalent of that code
|
|
*/
|
|
NOT_IMPLEMENTED();
|
|
return 0;
|
|
}
|
|
#endif /* VM_X86_ANY */
|
|
#else /* __GNUC__ */
|
|
#error No compiler defined for RDTSC
|
|
#endif /* __GNUC__ */
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* DEBUGBREAK --
|
|
*
|
|
* Does an int3 for MSVC / GCC, bkpt/brk for ARM. This is a macro to make
|
|
* sure int3 is always inlined.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifdef VM_ARM_32
|
|
#define DEBUGBREAK() __asm__("bkpt")
|
|
#elif defined(VM_ARM_64)
|
|
#define DEBUGBREAK() __asm__("brk #0")
|
|
#elif defined(_MSC_VER)
|
|
#define DEBUGBREAK() __debugbreak()
|
|
#else
|
|
#define DEBUGBREAK() __asm__("int $3")
|
|
#endif
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* {Clear,Set,Test}Bit{32,64} --
|
|
*
|
|
* Sets tests or clears a specified single bit in the provided variable.
|
|
*
|
|
* The index input value specifies which bit to modify and is 0-based.
|
|
* Index is truncated by hardware to a 5-bit or 6-bit offset for the
|
|
* 32 and 64-bit flavors, respectively, but input values are not validated
|
|
* with asserts to avoid include dependencies.
|
|
*
|
|
* 64-bit flavors are not handcrafted for 32-bit builds because they may
|
|
* defeat compiler optimizations.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
SetBit32(uint32 *var, unsigned index)
|
|
{
|
|
*var |= 1 << index;
|
|
}
|
|
|
|
static INLINE void
|
|
ClearBit32(uint32 *var, unsigned index)
|
|
{
|
|
*var &= ~(1 << index);
|
|
}
|
|
|
|
static INLINE void
|
|
SetBit64(uint64 *var, unsigned index)
|
|
{
|
|
*var |= CONST64U(1) << index;
|
|
}
|
|
|
|
static INLINE void
|
|
ClearBit64(uint64 *var, unsigned index)
|
|
{
|
|
*var &= ~(CONST64U(1) << index);
|
|
}
|
|
|
|
static INLINE Bool
|
|
TestBit32(const uint32 *var, unsigned index)
|
|
{
|
|
return (*var & (1 << index)) != 0;
|
|
}
|
|
|
|
static INLINE Bool
|
|
TestBit64(const uint64 *var, unsigned index)
|
|
{
|
|
return (*var & (CONST64U(1) << index)) != 0;
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* {Clear,Set,Complement,Test}BitVector --
|
|
*
|
|
* Sets, clears, complements, or tests a specified single bit in the
|
|
* provided array. The index input value specifies which bit to modify
|
|
* and is 0-based. Bit number can be +-2Gb (+-128MB) relative from 'var'
|
|
* variable.
|
|
*
|
|
* All functions return value of the bit before modification was performed.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE Bool
|
|
SetBitVector(void *var, int32 index)
|
|
{
|
|
#if defined(__GNUC__) && defined(VM_X86_ANY)
|
|
Bool bit;
|
|
__asm__ (
|
|
"bts %2, %1;"
|
|
"setc %0"
|
|
: "=qQm" (bit), "+m" (*(uint32 *)var)
|
|
: "rI" (index)
|
|
: "memory", "cc"
|
|
);
|
|
return bit;
|
|
#elif defined(_MSC_VER)
|
|
return _bittestandset((long *)var, index) != 0;
|
|
#else
|
|
Bool retVal = (((uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
|
|
((uint8 *)var)[index / 8] |= 1 << (index % 8);
|
|
return retVal;
|
|
#endif
|
|
}
|
|
|
|
static INLINE Bool
|
|
ClearBitVector(void *var, int32 index)
|
|
{
|
|
#if defined(__GNUC__) && defined(VM_X86_ANY)
|
|
Bool bit;
|
|
__asm__ (
|
|
"btr %2, %1;"
|
|
"setc %0"
|
|
: "=qQm" (bit), "+m" (*(uint32 *)var)
|
|
: "rI" (index)
|
|
: "memory", "cc"
|
|
);
|
|
return bit;
|
|
#elif defined(_MSC_VER)
|
|
return _bittestandreset((long *)var, index) != 0;
|
|
#else
|
|
Bool retVal = (((uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
|
|
((uint8 *)var)[index / 8] &= ~(1 << (index % 8));
|
|
return retVal;
|
|
#endif
|
|
}
|
|
|
|
static INLINE Bool
|
|
ComplementBitVector(void *var, int32 index)
|
|
{
|
|
#if defined(__GNUC__) && defined(VM_X86_ANY)
|
|
Bool bit;
|
|
__asm__ (
|
|
"btc %2, %1;"
|
|
"setc %0"
|
|
: "=qQm" (bit), "+m" (*(uint32 *)var)
|
|
: "rI" (index)
|
|
: "memory", "cc"
|
|
);
|
|
return bit;
|
|
#elif defined(_MSC_VER)
|
|
return _bittestandcomplement((long *)var, index) != 0;
|
|
#else
|
|
Bool retVal = (((uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
|
|
((uint8 *)var)[index / 8] ^= ~(1 << (index % 8));
|
|
return retVal;
|
|
#endif
|
|
}
|
|
|
|
static INLINE Bool
|
|
TestBitVector(const void *var, int32 index)
|
|
{
|
|
#if defined(__GNUC__) && defined(VM_X86_ANY)
|
|
Bool bit;
|
|
__asm__ (
|
|
"bt %2, %1;"
|
|
"setc %0"
|
|
: "=qQm" (bit)
|
|
: "m" (*(const uint32 *)var), "rI" (index)
|
|
: "cc"
|
|
);
|
|
return bit;
|
|
#elif defined _MSC_VER
|
|
return _bittest((long *)var, index) != 0;
|
|
#else
|
|
return (((const uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* RoundUpPow2_{64,32} --
|
|
*
|
|
* Rounds a value up to the next higher power of 2. Returns the original
|
|
* value if it is a power of 2. The next power of 2 for inputs {0, 1} is 1.
|
|
* The result is undefined for inputs above {2^63, 2^31} (but equal to 1
|
|
* in this implementation).
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE uint64
|
|
RoundUpPow2C64(uint64 value)
|
|
{
|
|
if (value <= 1 || value > (CONST64U(1) << 63)) {
|
|
return 1; // Match the assembly's undefined value for large inputs.
|
|
} else {
|
|
return (CONST64U(2) << mssb64_0(value - 1));
|
|
}
|
|
}
|
|
|
|
#if defined(__GNUC__) && defined(VM_X86_64)
|
|
static INLINE uint64
|
|
RoundUpPow2Asm64(uint64 value)
|
|
{
|
|
uint64 out = 2;
|
|
__asm__("lea -1(%[in]), %%rcx;" // rcx = value - 1. Preserve original.
|
|
"bsr %%rcx, %%rcx;" // rcx = log2(value - 1) if value != 1
|
|
// if value == 0, then rcx = 63
|
|
// if value == 1 then zf = 1, else zf = 0.
|
|
"rol %%cl, %[out];" // out = 2 << rcx (if rcx != -1)
|
|
// = 2^(log2(value - 1) + 1)
|
|
// if rcx == -1 (value == 0), out = 1
|
|
// zf is always unmodified.
|
|
"cmovz %[in], %[out]" // if value == 1 (zf == 1), write 1 to out.
|
|
: [out]"+r"(out) : [in]"r"(value) : "%rcx", "cc");
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
static INLINE uint64
|
|
RoundUpPow2_64(uint64 value)
|
|
{
|
|
#if defined(__GNUC__) && defined(VM_X86_64)
|
|
if (__builtin_constant_p(value)) {
|
|
return RoundUpPow2C64(value);
|
|
} else {
|
|
return RoundUpPow2Asm64(value);
|
|
}
|
|
#else
|
|
return RoundUpPow2C64(value);
|
|
#endif
|
|
}
|
|
|
|
static INLINE uint32
|
|
RoundUpPow2C32(uint32 value)
|
|
{
|
|
if (value <= 1 || value > (1U << 31)) {
|
|
return 1; // Match the assembly's undefined value for large inputs.
|
|
} else {
|
|
int mssb32 = mssb32_0(value - 1);
|
|
/* invariant: mssb32 >= 0 */
|
|
return (2U << (uint32)mssb32);
|
|
}
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
static INLINE uint32
|
|
RoundUpPow2Asm32(uint32 value)
|
|
{
|
|
#ifdef VM_ARM_32
|
|
uint32 out = 1;
|
|
// Note: None Thumb only!
|
|
// The value of the argument "value"
|
|
// will be affected!
|
|
__asm__("sub %[in], %[in], #1;" // r1 = value - 1 . if value == 0 then r1 = 0xFFFFFFFF
|
|
"clz %[in], %[in];" // r1 = log2(value - 1) if value != 1
|
|
// if value == 0 then r1 = 0
|
|
// if value == 1 then r1 = 32
|
|
"mov %[out], %[out], ror %[in]" // out = 2^(32 - r1)
|
|
// if out == 2^32 then out = 1 as it is right rotate
|
|
: [in]"+r"(value),[out]"+r"(out));
|
|
return out;
|
|
#elif defined(VM_ARM_64)
|
|
return RoundUpPow2C32(value);
|
|
#else
|
|
uint32 out = 2;
|
|
|
|
__asm__("lea -1(%[in]), %%ecx;" // ecx = value - 1. Preserve original.
|
|
"bsr %%ecx, %%ecx;" // ecx = log2(value - 1) if value != 1
|
|
// if value == 0, then ecx = 31
|
|
// if value == 1 then zf = 1, else zf = 0.
|
|
"rol %%cl, %[out];" // out = 2 << ecx (if ecx != -1)
|
|
// = 2^(log2(value - 1) + 1).
|
|
// if ecx == -1 (value == 0), out = 1
|
|
// zf is always unmodified
|
|
"cmovz %[in], %[out]" // if value == 1 (zf == 1), write 1 to out.
|
|
: [out]"+r"(out) : [in]"r"(value) : "%ecx", "cc");
|
|
return out;
|
|
#endif
|
|
}
|
|
#endif // __GNUC__
|
|
|
|
static INLINE uint32
|
|
RoundUpPow2_32(uint32 value)
|
|
{
|
|
#ifdef __GNUC__
|
|
if (__builtin_constant_p(value)) {
|
|
return RoundUpPow2C32(value);
|
|
} else {
|
|
return RoundUpPow2Asm32(value);
|
|
}
|
|
#else
|
|
return RoundUpPow2C32(value);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* PopCount32 --
|
|
*
|
|
* Counts "1" bits in a uint32.
|
|
*
|
|
* Results:
|
|
* Returns the number of bits set to 1.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE unsigned
|
|
PopCount32(uint32 value)
|
|
{
|
|
#if defined(__GNUC__) && !defined(FEWER_BUILTINS) && defined(__POPCNT__)
|
|
return __builtin_popcount(value);
|
|
#else
|
|
/*
|
|
* Attribution:
|
|
* This algorithm was copied from:
|
|
* http://www.aggregate.org/MAGIC#Population Count (Ones Count)
|
|
*
|
|
* A virtually identical version (but in assembly) appears in an
|
|
* AMD reference manual.
|
|
*
|
|
* No license appears in the original code, but the website
|
|
* header states:
|
|
*
|
|
* "None of the following coding tricks came from proprietary
|
|
* sources; further, we believe that each of the tricks we did
|
|
* not invent is essentially "standard engineering practice" in
|
|
* the specialized niche where it applies. Thus, although we
|
|
* have not conducted patent searches, etc., to confirm it, we
|
|
* believe that these are tricks that freely can be used for
|
|
* any purpose. Of course, The Aggregate accepts no
|
|
* responsibility for your use of these tricks; you must
|
|
* confirm that the trick does what you want and that you can
|
|
* use it as you intend. That said, we do intend to maintain
|
|
* this page by adding new algorithms and/or correcting
|
|
* existing entries. If you have any comments, please contact
|
|
* Professor Hank Dietz, http://aggregate.org/hankd/"
|
|
*
|
|
* "This document should be cited using something like the
|
|
* following bibtex entry:" (most recent retrieval date added)
|
|
*
|
|
* @techreport{magicalgorithms,
|
|
* author={Henry Gordon Dietz},
|
|
* title={{The Aggregate Magic Algorithms}},
|
|
* institution={University of Kentucky},
|
|
* howpublished={Aggregate.Org online technical report},
|
|
* URL={http://aggregate.org/MAGIC/},
|
|
* urldate={2016-01-27}
|
|
* }
|
|
*/
|
|
value -= ((value >> 1) & 0x55555555);
|
|
value = (((value >> 2) & 0x33333333) + (value & 0x33333333));
|
|
value = (((value >> 4) + value) & 0x0f0f0f0f);
|
|
value += (value >> 8);
|
|
value += (value >> 16);
|
|
return value & 0x0000003f;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* PopCount64 --
|
|
*
|
|
* Counts "1" bits in a uint64.
|
|
*
|
|
* Results:
|
|
* Returns the number of bits set to 1.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE unsigned
|
|
PopCount64(uint64 value)
|
|
{
|
|
#if defined(__GNUC__) && !defined(FEWER_BUILTINS) && defined(__POPCNT__)
|
|
#if defined(VM_X86_64)
|
|
return __builtin_popcountll(value);
|
|
#else
|
|
return PopCount32(value) + PopCount32(value >> 32);
|
|
#endif
|
|
#else
|
|
value -= (value >> 1) & 0x5555555555555555ULL;
|
|
value = ((value >> 2) & 0x3333333333333333ULL) +
|
|
(value & 0x3333333333333333ULL);
|
|
value = ((value >> 4) + value) & 0x0f0f0f0f0f0f0f0fULL;
|
|
value += value >> 8;
|
|
value += value >> 16;
|
|
value += value >> 32;
|
|
return (unsigned) (value & 0xff);
|
|
#endif
|
|
}
|
|
|
|
|
|
#if defined __cplusplus
|
|
} // extern "C"
|
|
#endif
|
|
|
|
#endif // _VM_BASIC_ASM_H_
|
|
|