blob: 3c18b263da65458fa02c5193075796c0fddf2f69 [file]
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Startup check that the CPU supports the extensions (SSE4.2, AVX2, BMI2,
// LZCNT) the binary is built with when enable_perfetto_x64_cpu_opt is set; if
// not, it prints an error and exits.
//
// Must be built for the baseline x86_64 ISA (its target subtracts
// config("x64_cpu_opt") in src/base/BUILD.gn), else the check could itself use
// instructions the CPU lacks. Context:
// https://github.com/google/perfetto/discussions/5138
#include <stdint.h>
#include "perfetto/base/build_config.h"
#include "perfetto/base/export.h"
#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
#include <stdio.h>
#include <unistd.h> // For _exit().
namespace {
// Preserve the %rbx register via %rdi to work around a clang bug
// https://bugs.llvm.org/show_bug.cgi?id=17907 (%rbx in an output constraint
// is not considered a clobbered register).
#define PERFETTO_GETCPUID(a, b, c, d, a_inp, c_inp) \
asm("mov %%rbx, %%rdi\n" \
"cpuid\n" \
"xchg %%rdi, %%rbx\n" \
: "=a"(a), "=D"(b), "=c"(c), "=d"(d) \
: "a"(a_inp), "2"(c_inp))
uint32_t GetXCR0EAX() {
uint32_t eax = 0, edx = 0;
asm("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
return eax;
}
// If we are building with -msse4 check that the CPU actually supports it.
// This file must be kept in sync with gn/standalone/BUILD.gn.
void PERFETTO_EXPORT_COMPONENT __attribute__((constructor))
CheckCpuOptimizations() {
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
PERFETTO_GETCPUID(eax, ebx, ecx, edx, 1, 0);
static constexpr uint64_t xcr0_xmm_mask = 0x2;
static constexpr uint64_t xcr0_ymm_mask = 0x4;
static constexpr uint64_t xcr0_avx_mask = xcr0_xmm_mask | xcr0_ymm_mask;
const bool have_popcnt = ecx & (1u << 23);
const bool have_sse4_2 = ecx & (1u << 20);
const bool have_avx =
// Does the OS save/restore XMM and YMM state?
(ecx & (1u << 27)) && // OS support XGETBV.
(ecx & (1u << 28)) && // AVX supported in hardware
((GetXCR0EAX() & xcr0_avx_mask) == xcr0_avx_mask);
// Get level 7 features (eax = 7 and ecx= 0), to check for AVX2 support.
// (See Intel 64 and IA-32 Architectures Software Developer's Manual
// Volume 2A: Instruction Set Reference, A-M CPUID).
PERFETTO_GETCPUID(eax, ebx, ecx, edx, 7, 0);
const bool have_avx2 = have_avx && ((ebx >> 5) & 0x1);
const bool have_bmi = (ebx >> 3) & 0x1;
const bool have_bmi2 = (ebx >> 8) & 0x1;
// Get extended features for LZCNT.
PERFETTO_GETCPUID(eax, ebx, ecx, edx, 0x80000001, 0);
const bool have_lzcnt = ecx & (1u << 5);
if (!have_sse4_2 || !have_popcnt || !have_avx2 || !have_bmi || !have_bmi2 ||
!have_lzcnt) {
fprintf(
stderr,
"This executable requires a x86_64 cpu that supports SSE4.2, BMI2, "
"AVX2 and LZCNT.\n"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
"On MacOS, this might be caused by running x86_64 binaries on arm64.\n"
"See https://github.com/google/perfetto/issues/294 for more.\n"
#endif
"Rebuild with enable_perfetto_x64_cpu_opt=false.\n");
_exit(126);
}
}
} // namespace
#endif // PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)