Move UTF8 checking functions into their own file

This commit is contained in:
John Keiser 2019-08-14 09:45:33 -07:00
parent 237b8865f5
commit 0042d9b406
12 changed files with 3547 additions and 1686 deletions

View File

@ -36,6 +36,11 @@ $SCRIPTPATH/include/simdjson/jsoncharutils.h
$SCRIPTPATH/include/simdjson/jsonformatutils.h
$SCRIPTPATH/include/simdjson/jsonioutil.h
$SCRIPTPATH/include/simdjson/simdprune_tables.h
$SCRIPTPATH/include/simdjson/simd_input.h
$SCRIPTPATH/include/simdjson/simd_input_haswell.h
$SCRIPTPATH/include/simdjson/simd_input_westmere.h
$SCRIPTPATH/include/simdjson/simd_input_arm64.h
$SCRIPTPATH/include/simdjson/simdutf8check.h
$SCRIPTPATH/include/simdjson/simdutf8check_haswell.h
$SCRIPTPATH/include/simdjson/simdutf8check_westmere.h
$SCRIPTPATH/include/simdjson/simdutf8check_arm64.h

View File

@ -10,12 +10,13 @@ namespace simdjson {
template <Architecture> struct simd_input;
template <Architecture T>
simd_input<T> fill_input(const uint8_t *ptr);
// a straightforward comparison of a mask against input.
template <Architecture T>
uint64_t cmp_mask_against_input(simd_input<T> in, uint8_t m);
template <Architecture T> simd_input<T> fill_input(const uint8_t *ptr);
// find all values less than or equal than the content of maxval (using unsigned
// arithmetic)
template <Architecture T>

View File

@ -0,0 +1,21 @@
#ifndef SIMDJSON_SIMDUTF8CHECK_H
#define SIMDJSON_SIMDUTF8CHECK_H
#include "simdjson/simdjson.h"
#include "simdjson/simd_input.h"
namespace simdjson {
// Holds the state required to perform check_utf8().
template <Architecture> struct utf8_checking_state;
template <Architecture T>
void check_utf8(simd_input<T> in, utf8_checking_state<T> &state);
// Checks if the utf8 validation has found any error.
template <Architecture T>
ErrorValues check_utf8_errors(utf8_checking_state<T> &state);
} // namespace simdjson
#endif // SIMDJSON_SIMDUTF8CHECK_H

View File

@ -7,6 +7,7 @@
#if defined(_ARM_NEON) || defined(__aarch64__) || \
(defined(_MSC_VER) && defined(_M_ARM64))
#include "simdjson/simdutf8check.h"
#include <arm_neon.h>
#include <cinttypes>
#include <cstddef>
@ -175,6 +176,64 @@ check_utf8_bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous,
previous->high_nibbles, has_error);
return pb;
}
template <>
struct utf8_checking_state<Architecture::ARM64> {
int8x16_t has_error{};
processed_utf_bytes previous{};
};
// Checks that all bytes are ascii
really_inline bool check_ascii_neon(simd_input<Architecture::ARM64> in) {
// checking if the most significant bit is always equal to 0.
uint8x16_t high_bit = vdupq_n_u8(0x80);
uint8x16_t t0 = vorrq_u8(in.i0, in.i1);
uint8x16_t t1 = vorrq_u8(in.i2, in.i3);
uint8x16_t t3 = vorrq_u8(t0, t1);
uint8x16_t t4 = vandq_u8(t3, high_bit);
uint64x2_t v64 = vreinterpretq_u64_u8(t4);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) == 0;
}
template <>
really_inline void check_utf8<Architecture::ARM64>(
simd_input<Architecture::ARM64> in,
utf8_checking_state<Architecture::ARM64> &state) {
if (check_ascii_neon(in)) {
// All bytes are ascii. Therefore the byte that was just before must be
// ascii too. We only check the byte that was just before simd_input. Nines
// are arbitrary values.
const int8x16_t verror =
(int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1};
state.has_error =
vorrq_s8(vreinterpretq_s8_u8(
vcgtq_s8(state.previous.carried_continuations, verror)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i0),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i1),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i2),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i3),
&(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::ARM64>(
utf8_checking_state<Architecture::ARM64> &state) {
uint64x2_t v64 = vreinterpretq_u64_s8(state.has_error);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) != 0 ? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
} // namespace simdjson
#endif
#endif

View File

@ -2,6 +2,7 @@
#define SIMDJSON_SIMDUTF8CHECK_HASWELL_H
#include "simdjson/portability.h"
#include "simdjson/simdutf8check.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@ -190,6 +191,48 @@ avx_check_utf8_bytes(__m256i current_bytes,
previous->high_nibbles, has_error);
return pb;
}
template <> struct utf8_checking_state<Architecture::HASWELL> {
__m256i has_error;
avx_processed_utf_bytes previous;
utf8_checking_state() {
has_error = _mm256_setzero_si256();
previous.raw_bytes = _mm256_setzero_si256();
previous.high_nibbles = _mm256_setzero_si256();
previous.carried_continuations = _mm256_setzero_si256();
}
};
template <>
really_inline void check_utf8<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in,
utf8_checking_state<Architecture::HASWELL> &state) {
__m256i high_bit = _mm256_set1_epi8(0x80u);
if ((_mm256_testz_si256(_mm256_or_si256(in.lo, in.hi), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error = _mm256_or_si256(
_mm256_cmpgt_epi8(state.previous.carried_continuations,
_mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
avx_check_utf8_bytes(in.lo, &(state.previous), &(state.has_error));
state.previous =
avx_check_utf8_bytes(in.hi, &(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::HASWELL>(
utf8_checking_state<Architecture::HASWELL> &state) {
return _mm256_testz_si256(state.has_error, state.has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
} // namespace simdjson
UNTARGET_REGION // haswell

View File

@ -2,6 +2,7 @@
#define SIMDJSON_SIMDUTF8CHECK_WESTMERE_H
#include "simdjson/portability.h"
#include "simdjson/simdutf8check.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@ -161,6 +162,61 @@ check_utf8_bytes(__m128i current_bytes, struct processed_utf_bytes *previous,
previous->high_nibbles, has_error);
return pb;
}
template <>
struct utf8_checking_state<Architecture::WESTMERE> {
__m128i has_error = _mm_setzero_si128();
processed_utf_bytes previous{
_mm_setzero_si128(), // raw_bytes
_mm_setzero_si128(), // high_nibbles
_mm_setzero_si128() // carried_continuations
};
};
template <>
really_inline void check_utf8<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in,
utf8_checking_state<Architecture::WESTMERE> &state) {
__m128i high_bit = _mm_set1_epi8(0x80u);
if ((_mm_testz_si128(_mm_or_si128(in.v0, in.v1), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error =
_mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
check_utf8_bytes(in.v0, &(state.previous), &(state.has_error));
state.previous =
check_utf8_bytes(in.v1, &(state.previous), &(state.has_error));
}
if ((_mm_testz_si128(_mm_or_si128(in.v2, in.v3), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error =
_mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
check_utf8_bytes(in.v2, &(state.previous), &(state.has_error));
state.previous =
check_utf8_bytes(in.v3, &(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::WESTMERE>(
utf8_checking_state<Architecture::WESTMERE> &state) {
return _mm_testz_si128(state.has_error, state.has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
} // namespace simdjson
UNTARGET_REGION // westmere

View File

@ -18,63 +18,6 @@ compute_quote_mask<Architecture::ARM64>(uint64_t quote_bits) {
#endif
}
template <>
struct utf8_checking_state<Architecture::ARM64> {
int8x16_t has_error{};
processed_utf_bytes previous{};
};
// Checks that all bytes are ascii
really_inline bool check_ascii_neon(simd_input<Architecture::ARM64> in) {
// checking if the most significant bit is always equal to 0.
uint8x16_t high_bit = vdupq_n_u8(0x80);
uint8x16_t t0 = vorrq_u8(in.i0, in.i1);
uint8x16_t t1 = vorrq_u8(in.i2, in.i3);
uint8x16_t t3 = vorrq_u8(t0, t1);
uint8x16_t t4 = vandq_u8(t3, high_bit);
uint64x2_t v64 = vreinterpretq_u64_u8(t4);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) == 0;
}
template <>
really_inline void check_utf8<Architecture::ARM64>(
simd_input<Architecture::ARM64> in,
utf8_checking_state<Architecture::ARM64> &state) {
if (check_ascii_neon(in)) {
// All bytes are ascii. Therefore the byte that was just before must be
// ascii too. We only check the byte that was just before simd_input. Nines
// are arbitrary values.
const int8x16_t verror =
(int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1};
state.has_error =
vorrq_s8(vreinterpretq_s8_u8(
vcgtq_s8(state.previous.carried_continuations, verror)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i0),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i1),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i2),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i3),
&(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::ARM64>(
utf8_checking_state<Architecture::ARM64> &state) {
uint64x2_t v64 = vreinterpretq_u64_s8(state.has_error);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) != 0 ? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
template <>
really_inline void find_whitespace_and_structurals<Architecture::ARM64>(
simd_input<Architecture::ARM64> in, uint64_t &whitespace,

View File

@ -20,47 +20,6 @@ compute_quote_mask<Architecture::HASWELL>(uint64_t quote_bits) {
return quote_mask;
}
template <> struct utf8_checking_state<Architecture::HASWELL> {
__m256i has_error;
avx_processed_utf_bytes previous;
utf8_checking_state() {
has_error = _mm256_setzero_si256();
previous.raw_bytes = _mm256_setzero_si256();
previous.high_nibbles = _mm256_setzero_si256();
previous.carried_continuations = _mm256_setzero_si256();
}
};
template <>
really_inline void check_utf8<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in,
utf8_checking_state<Architecture::HASWELL> &state) {
__m256i high_bit = _mm256_set1_epi8(0x80u);
if ((_mm256_testz_si256(_mm256_or_si256(in.lo, in.hi), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error = _mm256_or_si256(
_mm256_cmpgt_epi8(state.previous.carried_continuations,
_mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
avx_check_utf8_bytes(in.lo, &(state.previous), &(state.has_error));
state.previous =
avx_check_utf8_bytes(in.hi, &(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::HASWELL>(
utf8_checking_state<Architecture::HASWELL> &state) {
return _mm256_testz_si256(state.has_error, state.has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
template <>
really_inline void find_whitespace_and_structurals<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in, uint64_t &whitespace,

View File

@ -17,59 +17,6 @@ compute_quote_mask<Architecture::WESTMERE>(uint64_t quote_bits) {
_mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFFu), 0));
}
template <> struct utf8_checking_state<Architecture::WESTMERE> {
__m128i has_error = _mm_setzero_si128();
processed_utf_bytes previous{
_mm_setzero_si128(), // raw_bytes
_mm_setzero_si128(), // high_nibbles
_mm_setzero_si128() // carried_continuations
};
};
template <>
really_inline void check_utf8<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in,
utf8_checking_state<Architecture::WESTMERE> &state) {
__m128i high_bit = _mm_set1_epi8(0x80u);
if ((_mm_testz_si128(_mm_or_si128(in.v0, in.v1), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error =
_mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
check_utf8_bytes(in.v0, &(state.previous), &(state.has_error));
state.previous =
check_utf8_bytes(in.v1, &(state.previous), &(state.has_error));
}
if ((_mm_testz_si128(_mm_or_si128(in.v2, in.v3), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error =
_mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
check_utf8_bytes(in.v2, &(state.previous), &(state.has_error));
state.previous =
check_utf8_bytes(in.v3, &(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::WESTMERE>(
utf8_checking_state<Architecture::WESTMERE> &state) {
return _mm_testz_si128(state.has_error, state.has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
template <>
really_inline void find_whitespace_and_structurals<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in, uint64_t &whitespace,

View File

@ -1,4 +1,4 @@
/* auto-generated on Sun Aug 4 15:43:41 EDT 2019. Do not edit! */
/* auto-generated on Wed Aug 14 10:31:26 DST 2019. Do not edit! */
#include <iostream>
#include "simdjson.h"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff