morningman commented on a change in pull request #8042: URL: https://github.com/apache/incubator-doris/pull/8042#discussion_r806436781
########## File path: be/src/service/doris_main.cpp ########## @@ -74,6 +79,199 @@ static void thrift_output(const char* x) { } // namespace doris +// These code is referenced from clickhouse Review comment: Can these code be moved to a separated file. like `util/instruction_utils.hpp`? ########## File path: be/src/service/doris_main.cpp ########## @@ -74,6 +79,199 @@ static void thrift_output(const char* x) { } // namespace doris +// These code is referenced from clickhouse +// It is used to check the SIMD instructions +enum class InstructionFail { + NONE = 0, + SSE3 = 1, + SSSE3 = 2, + SSE4_1 = 3, + SSE4_2 = 4, + POPCNT = 5, + AVX = 6, + AVX2 = 7, + AVX512 = 8, + ARM_NEON = 9 +}; + +auto instruction_fail_to_string(InstructionFail fail) +{ + switch (fail) + { +#define ret(x) return std::make_tuple(STDERR_FILENO, x, ARRAY_SIZE(x) - 1) + case InstructionFail::NONE: + ret("NONE"); + case InstructionFail::SSE3: + ret("SSE3"); + case InstructionFail::SSSE3: + ret("SSSE3"); + case InstructionFail::SSE4_1: + ret("SSE4.1"); + case InstructionFail::SSE4_2: + ret("SSE4.2"); + case InstructionFail::POPCNT: + ret("POPCNT"); + case InstructionFail::AVX: + ret("AVX"); + case InstructionFail::AVX2: + ret("AVX2"); + case InstructionFail::AVX512: + ret("AVX512"); + case InstructionFail::ARM_NEON: + ret("ARM_NEON"); + } + __builtin_unreachable(); +} + + +sigjmp_buf jmpbuf; + +void sig_ill_check_handler(int, siginfo_t *, void *) +{ + siglongjmp(jmpbuf, 1); +} + +/// Check if necessary SSE extensions are available by trying to execute some sse instructions. +/// If instruction is unavailable, SIGILL will be sent by kernel. +void check_required_instructions_impl(volatile InstructionFail & fail) +{ +#if defined(__SSE3__) + fail = InstructionFail::SSE3; + __asm__ volatile ("addsubpd %%xmm0, %%xmm0" : : : "xmm0"); +#endif + +#if defined(__SSSE3__) + fail = InstructionFail::SSSE3; + __asm__ volatile ("pabsw %%xmm0, %%xmm0" : : : "xmm0"); + +#endif + +#if defined(__SSE4_1__) + fail = InstructionFail::SSE4_1; + __asm__ volatile ("pmaxud %%xmm0, %%xmm0" : : : "xmm0"); +#endif + +#if defined(__SSE4_2__) + fail = InstructionFail::SSE4_2; + __asm__ volatile ("pcmpgtq %%xmm0, %%xmm0" : : : "xmm0"); +#endif + + /// Defined by -msse4.2 +#if defined(__POPCNT__) + fail = InstructionFail::POPCNT; + { + uint64_t a = 0; + uint64_t b = 0; + __asm__ volatile ("popcnt %1, %0" : "=r"(a) :"r"(b) :); + } +#endif + +#if defined(__AVX__) + fail = InstructionFail::AVX; + __asm__ volatile ("vaddpd %%ymm0, %%ymm0, %%ymm0" : : : "ymm0"); +#endif + +#if defined(__AVX2__) + fail = InstructionFail::AVX2; + __asm__ volatile ("vpabsw %%ymm0, %%ymm0" : : : "ymm0"); +#endif + +#if defined(__AVX512__) + fail = InstructionFail::AVX512; + __asm__ volatile ("vpabsw %%zmm0, %%zmm0" : : : "zmm0"); +#endif + +#if defined(__ARM_NEON__) + fail = InstructionFail::ARM_NEON; + __asm__ volatile ("vadd.i32 q8, q8, q8" : : : "q8"); +#endif + + fail = InstructionFail::NONE; +} + +bool write_retry(int fd, const char * data, size_t size) +{ + if (!size) + size = strlen(data); + + while (size != 0) + { + ssize_t res = ::write(fd, data, size); + + if ((-1 == res || 0 == res) && errno != EINTR) + return false; + + if (res > 0) + { + data += res; + size -= res; + } + } + + return true; +} + +/// Macros to avoid using strlen(), since it may fail if SSE is not supported. +#define WRITE_ERROR(data) do \ + { \ + static_assert(__builtin_constant_p(data)); \ + if (!write_retry(STDERR_FILENO, data, ARRAY_SIZE(data) - 1)) \ + _Exit(1); \ + } while (false) + +/// Check SSE and others instructions availability. Calls exit on fail. +/// This function must be called as early as possible, even before main, because static initializers may use unavailable instructions. +void check_required_instructions() +{ + struct sigaction sa{}; + struct sigaction sa_old{}; + sa.sa_sigaction = sig_ill_check_handler; + sa.sa_flags = SA_SIGINFO; + auto signal = SIGILL; + if (sigemptyset(&sa.sa_mask) != 0 + || sigaddset(&sa.sa_mask, signal) != 0 + || sigaction(signal, &sa, &sa_old) != 0) + { + /// You may wonder about strlen. + /// Typical implementation of strlen is using SSE4.2 or AVX2. + /// But this is not the case because it's compiler builtin and is executed at compile time. + + WRITE_ERROR("Can not set signal handler\n"); + _Exit(1); + } + + volatile InstructionFail fail = InstructionFail::NONE; + + if (sigsetjmp(jmpbuf, 1)) + { + WRITE_ERROR("Instruction check fail. The CPU does not support "); + if (!std::apply(write_retry, instruction_fail_to_string(fail))) + _Exit(1); + WRITE_ERROR(" instruction set.\n"); Review comment: Add following to guide the user: ``` WRITE_ERROR("For AVX2 support, you need to rebuild the Doris BE with: "); WRITE_ERROR("USE_AVX2=0 sh build.sh --be\n"); ``` ########## File path: be/src/service/doris_main.cpp ########## @@ -74,6 +79,199 @@ static void thrift_output(const char* x) { } // namespace doris +// These code is referenced from clickhouse Review comment: Ok I see. I've checked and it works. ########## File path: be/src/service/doris_main.cpp ########## @@ -74,6 +79,199 @@ static void thrift_output(const char* x) { } // namespace doris +// These code is referenced from clickhouse +// It is used to check the SIMD instructions +enum class InstructionFail { + NONE = 0, + SSE3 = 1, + SSSE3 = 2, + SSE4_1 = 3, + SSE4_2 = 4, + POPCNT = 5, + AVX = 6, + AVX2 = 7, + AVX512 = 8, + ARM_NEON = 9 +}; + +auto instruction_fail_to_string(InstructionFail fail) +{ + switch (fail) + { +#define ret(x) return std::make_tuple(STDERR_FILENO, x, ARRAY_SIZE(x) - 1) + case InstructionFail::NONE: + ret("NONE"); + case InstructionFail::SSE3: + ret("SSE3"); + case InstructionFail::SSSE3: + ret("SSSE3"); + case InstructionFail::SSE4_1: + ret("SSE4.1"); + case InstructionFail::SSE4_2: + ret("SSE4.2"); + case InstructionFail::POPCNT: + ret("POPCNT"); + case InstructionFail::AVX: + ret("AVX"); + case InstructionFail::AVX2: + ret("AVX2"); + case InstructionFail::AVX512: + ret("AVX512"); + case InstructionFail::ARM_NEON: + ret("ARM_NEON"); + } + __builtin_unreachable(); +} + + +sigjmp_buf jmpbuf; + +void sig_ill_check_handler(int, siginfo_t *, void *) +{ + siglongjmp(jmpbuf, 1); +} + +/// Check if necessary SSE extensions are available by trying to execute some sse instructions. +/// If instruction is unavailable, SIGILL will be sent by kernel. +void check_required_instructions_impl(volatile InstructionFail & fail) +{ +#if defined(__SSE3__) + fail = InstructionFail::SSE3; + __asm__ volatile ("addsubpd %%xmm0, %%xmm0" : : : "xmm0"); +#endif + +#if defined(__SSSE3__) + fail = InstructionFail::SSSE3; + __asm__ volatile ("pabsw %%xmm0, %%xmm0" : : : "xmm0"); + +#endif + +#if defined(__SSE4_1__) + fail = InstructionFail::SSE4_1; + __asm__ volatile ("pmaxud %%xmm0, %%xmm0" : : : "xmm0"); +#endif + +#if defined(__SSE4_2__) + fail = InstructionFail::SSE4_2; + __asm__ volatile ("pcmpgtq %%xmm0, %%xmm0" : : : "xmm0"); +#endif + + /// Defined by -msse4.2 +#if defined(__POPCNT__) + fail = InstructionFail::POPCNT; + { + uint64_t a = 0; + uint64_t b = 0; + __asm__ volatile ("popcnt %1, %0" : "=r"(a) :"r"(b) :); + } +#endif + +#if defined(__AVX__) + fail = InstructionFail::AVX; + __asm__ volatile ("vaddpd %%ymm0, %%ymm0, %%ymm0" : : : "ymm0"); +#endif + +#if defined(__AVX2__) + fail = InstructionFail::AVX2; + __asm__ volatile ("vpabsw %%ymm0, %%ymm0" : : : "ymm0"); +#endif + +#if defined(__AVX512__) + fail = InstructionFail::AVX512; + __asm__ volatile ("vpabsw %%zmm0, %%zmm0" : : : "zmm0"); +#endif + +#if defined(__ARM_NEON__) + fail = InstructionFail::ARM_NEON; + __asm__ volatile ("vadd.i32 q8, q8, q8" : : : "q8"); +#endif + + fail = InstructionFail::NONE; +} + +bool write_retry(int fd, const char * data, size_t size) +{ + if (!size) + size = strlen(data); + + while (size != 0) + { + ssize_t res = ::write(fd, data, size); + + if ((-1 == res || 0 == res) && errno != EINTR) + return false; + + if (res > 0) + { + data += res; + size -= res; + } + } + + return true; +} + +/// Macros to avoid using strlen(), since it may fail if SSE is not supported. +#define WRITE_ERROR(data) do \ + { \ + static_assert(__builtin_constant_p(data)); \ + if (!write_retry(STDERR_FILENO, data, ARRAY_SIZE(data) - 1)) \ + _Exit(1); \ + } while (false) + +/// Check SSE and others instructions availability. Calls exit on fail. +/// This function must be called as early as possible, even before main, because static initializers may use unavailable instructions. +void check_required_instructions() +{ + struct sigaction sa{}; + struct sigaction sa_old{}; + sa.sa_sigaction = sig_ill_check_handler; + sa.sa_flags = SA_SIGINFO; + auto signal = SIGILL; + if (sigemptyset(&sa.sa_mask) != 0 + || sigaddset(&sa.sa_mask, signal) != 0 + || sigaction(signal, &sa, &sa_old) != 0) + { + /// You may wonder about strlen. + /// Typical implementation of strlen is using SSE4.2 or AVX2. + /// But this is not the case because it's compiler builtin and is executed at compile time. + + WRITE_ERROR("Can not set signal handler\n"); + _Exit(1); + } + + volatile InstructionFail fail = InstructionFail::NONE; + + if (sigsetjmp(jmpbuf, 1)) + { + WRITE_ERROR("Instruction check fail. The CPU does not support "); + if (!std::apply(write_retry, instruction_fail_to_string(fail))) + _Exit(1); + WRITE_ERROR(" instruction set.\n"); Review comment: Add following to guide the user: ``` WRITE_ERROR("If CPU does not support AVX2, you need to rebuild the Doris BE with: "); WRITE_ERROR("USE_AVX2=0 sh build.sh --be\n"); ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org