8379811: Disable AOTCodeCache if Assembly intrinsics use differs from Production use

Reviewed-by: kvn, asmehra
This commit is contained in:
Andrew Dinn 2026-04-01 07:47:17 +00:00
parent 3dcf2a3bac
commit 32e8aa4582
2 changed files with 384 additions and 22 deletions

View File

@ -380,37 +380,110 @@ void AOTCodeCache::init_early_c1_table() {
}
}
// macro to record which flags are set -- flag_type selects the
// relevant accessor e.g. set_flag, set_x86_flag, set_x86_use_flag.
// n.b. flag_enum_name and global_flag_name are both needed because we
// don't have consistent conventions for naming global flags e.g.
// EnableContended vs UseMulAddIntrinsic vs UseCRC32Intrinsics
#define RECORD_FLAG(flag_type, flag_enum_name, global_flag_name) \
if (global_flag_name) { \
set_ ## flag_type ## flag(flag_enum_name); \
}
void AOTCodeCache::Config::record(uint cpu_features_offset) {
_flags = 0;
#ifdef ASSERT
_flags |= debugVM;
set_flag(debugVM);
#endif
if (UseCompressedOops) {
_flags |= compressedOops;
}
if (UseTLAB) {
_flags |= useTLAB;
}
RECORD_FLAG(, compressedOops, UseCompressedOops);
RECORD_FLAG(, useTLAB, UseTLAB);
if (JavaAssertions::systemClassDefault()) {
_flags |= systemClassAssertions;
set_flag(systemClassAssertions);
}
if (JavaAssertions::userClassDefault()) {
_flags |= userClassAssertions;
}
if (EnableContended) {
_flags |= enableContendedPadding;
}
if (RestrictContended) {
_flags |= restrictContendedPadding;
set_flag(userClassAssertions);
}
RECORD_FLAG(, enableContendedPadding, EnableContended);
RECORD_FLAG(, restrictContendedPadding, RestrictContended);
_compressedOopShift = CompressedOops::shift();
_compressedOopBase = CompressedOops::base();
_compressedKlassShift = CompressedKlassPointers::shift();
_contendedPaddingWidth = ContendedPaddingWidth;
_gc = (uint)Universe::heap()->kind();
_optoLoopAlignment = (uint)OptoLoopAlignment;
_codeEntryAlignment = (uint)CodeEntryAlignment;
_allocatePrefetchLines = (uint)AllocatePrefetchLines;
_allocateInstancePrefetchLines = (uint)AllocateInstancePrefetchLines;
_allocatePrefetchDistance = (uint)AllocatePrefetchDistance;
_allocatePrefetchStepSize = (uint)AllocatePrefetchStepSize;
_use_intrinsics_flags = 0;
RECORD_FLAG(use_, useCRC32, UseCRC32Intrinsics);
RECORD_FLAG(use_, useCRC32C, UseCRC32CIntrinsics);
#ifdef COMPILER2
_maxVectorSize = (uint)MaxVectorSize;
_arrayOperationPartialInlineSize = (uint)ArrayOperationPartialInlineSize;
RECORD_FLAG(use_, useMultiplyToLen, UseMultiplyToLenIntrinsic);
RECORD_FLAG(use_, useSquareToLen, UseSquareToLenIntrinsic);
RECORD_FLAG(use_, useMulAdd, UseMulAddIntrinsic);
RECORD_FLAG(use_, useMontgomeryMultiply, UseMontgomeryMultiplyIntrinsic);
RECORD_FLAG(use_, useMontgomerySquare, UseMontgomerySquareIntrinsic);
#endif // COMPILER2
RECORD_FLAG(use_, useChaCha20, UseChaCha20Intrinsics);
RECORD_FLAG(use_, useDilithium, UseDilithiumIntrinsics);
RECORD_FLAG(use_, useKyber, UseKyberIntrinsics);
RECORD_FLAG(use_, useBASE64, UseBASE64Intrinsics);
RECORD_FLAG(use_, useAdler32, UseAdler32Intrinsics);
RECORD_FLAG(use_, useAES, UseAESIntrinsics);
RECORD_FLAG(use_, useAESCTR, UseAESCTRIntrinsics);
RECORD_FLAG(use_, useGHASH, UseGHASHIntrinsics);
RECORD_FLAG(use_, useMD5, UseMD5Intrinsics);
RECORD_FLAG(use_, useSHA1, UseSHA1Intrinsics);
RECORD_FLAG(use_, useSHA256, UseSHA256Intrinsics);
RECORD_FLAG(use_, useSHA512, UseSHA512Intrinsics);
RECORD_FLAG(use_, useSHA3, UseSHA3Intrinsics);
RECORD_FLAG(use_, usePoly1305, UsePoly1305Intrinsics);
RECORD_FLAG(use_, useVectorizedMismatch,UseVectorizedMismatchIntrinsic );
RECORD_FLAG(use_, useSecondarySupersTable, UseSecondarySupersTable);
#if defined(X86) && !defined(ZERO)
_avx3threshold = (uint)AVX3Threshold;
_useAVX = (uint)UseAVX;
_x86_flags = 0;
RECORD_FLAG(x86_, x86_enableX86ECoreOpts, EnableX86ECoreOpts);
RECORD_FLAG(x86_, x86_useUnalignedLoadStores, UseUnalignedLoadStores);
RECORD_FLAG(x86_, x86_useAPX, UseAPX);
_x86_use_intrinsics_flags = 0;
RECORD_FLAG(x86_use_, x86_useLibm, UseLibmIntrinsic);
RECORD_FLAG(x86_use_, x86_useIntPoly, UseIntPolyIntrinsics);
#endif // defined(X86) && !defined(ZERO)
#if defined(AARCH64) && !defined(ZERO)
_prefetchCopyIntervalInBytes = (uint)PrefetchCopyIntervalInBytes;
_blockZeroingLowLimit = (uint)BlockZeroingLowLimit;
_softwarePrefetchHintDistance = (uint)SoftwarePrefetchHintDistance;
_useSVE = (uint)UseSVE;
_aarch64_flags = 0;
RECORD_FLAG(aarch64_, aarch64_avoidUnalignedAccesses, AvoidUnalignedAccesses);
RECORD_FLAG(aarch64_, aarch64_useSIMDForMemoryOps, UseSIMDForMemoryOps);
RECORD_FLAG(aarch64_, aarch64_useSIMDForArrayEquals, UseSIMDForArrayEquals);
RECORD_FLAG(aarch64_, aarch64_useSIMDForSHA3, UseSIMDForSHA3Intrinsic);
RECORD_FLAG(aarch64_, aarch64_useLSE, UseLSE);
_aarch64_use_intrinsics_flags = 0;
RECORD_FLAG(aarch64_use_, aarch64_useBlockZeroing, UseBlockZeroing);
RECORD_FLAG(aarch64_use_, aarch64_useSIMDForBigIntegerShift, UseSIMDForBigIntegerShiftIntrinsics);
RECORD_FLAG(aarch64_use_, aarch64_useSimpleArrayEquals, UseSimpleArrayEquals);
RECORD_FLAG(aarch64_use_, aarch64_useSecondarySupersCache, UseSecondarySupersCache);
#endif // defined(AARCH64) && !defined(ZERO)
#if INCLUDE_JVMCI
_enableJVMCI = (uint)EnableJVMCI;
#endif
_cpu_features_offset = cpu_features_offset;
}
#undef RECORD_FLAG
bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const {
LogStreamHandle(Debug, aot, codecache, init) log;
uint offset = _cpu_features_offset;
@ -451,15 +524,27 @@ bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const {
return true;
}
// macro to do *standard* flag eq checks -- flag_type selects the
// relevant accessor e.g. test_flag, test_x86_flag, test_x86_use_flag.
// n.b. flag_enum_name and global_flag_name are both needed because we
// don't have consistent conventions for naming global flags e.g.
// EnableContended vs UseMulAddIntrinsic vs UseCRC32Intrinsics
#define CHECK_FLAG(flag_type, flag_enum_name, global_flag_name) \
if (test_ ## flag_type ## flag(flag_enum_name) != global_flag_name) { \
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with " # global_flag_name " = %s vs current %s" , (global_flag_name ? "false" : "true"), (global_flag_name ? "true" : "false")); \
return false; \
}
bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
// First checks affect all cached AOT code
#ifdef ASSERT
if ((_flags & debugVM) == 0) {
if (!test_flag(debugVM)) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by product VM, it can't be used by debug VM");
return false;
}
#else
if ((_flags & debugVM) != 0) {
if (test_flag(debugVM)) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by debug VM, it can't be used by product VM");
return false;
}
@ -476,9 +561,195 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
return false;
}
// check CPU features before checking flags that may be
// auto-configured in response to them
if (!verify_cpu_features(cache)) {
return false;
}
// change to EnableContended can affect validity of nmethods
CHECK_FLAG(, enableContendedPadding, EnableContended);
// change to RestrictContended can affect validity of nmethods
CHECK_FLAG(, restrictContendedPadding, RestrictContended);
// Tests for config options which might affect validity of adapters,
// stubs or nmethods. Currently we take a pessemistic stand and
// drop the whole cache if any of these are changed.
// change to opto alignment can affect performance of array copy
// stubs and nmethods
if (_optoLoopAlignment != (uint)OptoLoopAlignment) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with OptoLoopAlignment = %d vs current %d", (int)_optoLoopAlignment, (int)OptoLoopAlignment);
return false;
}
// change to CodeEntryAlignment can affect performance of array
// copy stubs and nmethods
if (_codeEntryAlignment != CodeEntryAlignment) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CodeEntryAlignment = %d vs current %d", _codeEntryAlignment, CodeEntryAlignment);
return false;
}
// changing Prefetch configuration can affect validity of nmethods
// and stubs
if (_allocatePrefetchLines != (uint)AllocatePrefetchLines) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocatePrefetchLines, (int)AllocatePrefetchLines);
return false;
}
if (_allocateInstancePrefetchLines != (uint)AllocateInstancePrefetchLines) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocateInstancePrefetchLines, (int)AllocateInstancePrefetchLines);
return false;
}
if (_allocatePrefetchDistance != (uint)AllocatePrefetchDistance) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocatePrefetchDistance, (int)AllocatePrefetchDistance);
return false;
}
if (_allocatePrefetchStepSize != (uint)AllocatePrefetchStepSize) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocatePrefetchStepSize, (int)AllocatePrefetchStepSize);
return false;
}
// check intrinsic use settings are compatible
CHECK_FLAG(use_, useCRC32, UseCRC32Intrinsics);
CHECK_FLAG(use_, useCRC32C, UseCRC32CIntrinsics);
#ifdef COMPILER2
// change to MaxVectorSize can affect validity of array copy/fill
// stubs
if (_maxVectorSize != (uint)MaxVectorSize) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with MaxVectorSize = %d vs current %d", (int)_maxVectorSize, (int)MaxVectorSize);
return false;
}
// changing ArrayOperationPartialInlineSize can affect validity of
// nmethods and stubs
if (_arrayOperationPartialInlineSize != (uint)ArrayOperationPartialInlineSize) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with ArrayOperationPartialInlineSize = %d vs current %d", (int)_arrayOperationPartialInlineSize, (int)ArrayOperationPartialInlineSize);
return false;
}
CHECK_FLAG(use_, useMultiplyToLen, UseMultiplyToLenIntrinsic);
CHECK_FLAG(use_, useSquareToLen, UseSquareToLenIntrinsic);
CHECK_FLAG(use_, useMulAdd, UseMulAddIntrinsic);
CHECK_FLAG(use_, useMontgomeryMultiply,UseMontgomeryMultiplyIntrinsic);
CHECK_FLAG(use_, useMontgomerySquare, UseMontgomerySquareIntrinsic);
#endif // COMPILER2
CHECK_FLAG(use_, useChaCha20, UseChaCha20Intrinsics);
CHECK_FLAG(use_, useDilithium, UseDilithiumIntrinsics);
CHECK_FLAG(use_, useKyber, UseKyberIntrinsics);
CHECK_FLAG(use_, useBASE64, UseBASE64Intrinsics);
CHECK_FLAG(use_, useAES, UseAESIntrinsics);
CHECK_FLAG(use_, useAESCTR, UseAESCTRIntrinsics);
CHECK_FLAG(use_, useGHASH, UseGHASHIntrinsics);
CHECK_FLAG(use_, useMD5, UseMD5Intrinsics);
CHECK_FLAG(use_, useSHA1, UseSHA1Intrinsics);
CHECK_FLAG(use_, useSHA256, UseSHA256Intrinsics);
CHECK_FLAG(use_, useSHA512, UseSHA512Intrinsics);
CHECK_FLAG(use_, useSHA3, UseSHA3Intrinsics);
CHECK_FLAG(use_, usePoly1305, UsePoly1305Intrinsics);
CHECK_FLAG(use_, useVectorizedMismatch, UseVectorizedMismatchIntrinsic);
CHECK_FLAG(use_, useSecondarySupersTable, UseSecondarySupersTable);
#if defined(X86) && !defined(ZERO)
// change to AVX3Threshold may affect validity of array copy stubs
// and nmethods
if (_avx3threshold != (uint)AVX3Threshold) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with AVX3Threshold = %d vs current %d", (int)_avx3threshold, AVX3Threshold);
return false;
}
// change to UseAVX may affect validity of array copy stubs and
// nmethods
if (_useAVX != (uint)UseAVX) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with useAVX = %d vs current %d", (int)_useAVX, UseAVX);
return false;
}
// change to EnableX86ECoreOpts may affect validity of nmethods
CHECK_FLAG(x86_, x86_enableX86ECoreOpts, EnableX86ECoreOpts);
// switching off UseUnalignedLoadStores can affect validity of fill
// stubs
if (test_x86_flag(x86_useUnalignedLoadStores) && !UseUnalignedLoadStores) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseUnalignedLoadStores = true vs current = false");
return false;
}
// change to UseAPX can affect validity of nmethods and stubs
CHECK_FLAG(x86_, x86_useAPX, UseAPX);
// check x86-specific intrinsic use settings are compatible
CHECK_FLAG(x86_use_, x86_useLibm, UseLibmIntrinsic);
CHECK_FLAG(x86_use_, x86_useIntPoly, UseIntPolyIntrinsics);
#endif // defined(X86) && !defined(ZERO)
#if defined(AARCH64) && !defined(ZERO)
// change to PrefetchCopyIntervalInBytes may affect validity of
// array copy stubs
if (_prefetchCopyIntervalInBytes != (uint)PrefetchCopyIntervalInBytes) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with PrefetchCopyIntervalInBytes = %d vs current %d", (int)_prefetchCopyIntervalInBytes, (int)PrefetchCopyIntervalInBytes);
return false;
}
// change to BlockZeroingLowLimit may affect validity of array fill
// stubs
if (_blockZeroingLowLimit != (uint)BlockZeroingLowLimit) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with BlockZeroingLowLimit = %d vs current %d", (int)_blockZeroingLowLimit, (int)BlockZeroingLowLimit);
return false;
}
// change to SoftwarePrefetchHintDistance may affect validity of array fill
// stubs
if (_softwarePrefetchHintDistance != (uint)SoftwarePrefetchHintDistance) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with SoftwarePrefetchHintDistance = %d vs current %d", (int)_softwarePrefetchHintDistance, (int)SoftwarePrefetchHintDistance);
return false;
}
// change to UseSVE may affect validity of stubs and nmethods
if (_useSVE != (uint)UseSVE) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseSVE = %d vs current %d",(int)_useSVE, UseSVE);
return false;
}
// switching on AvoidUnalignedAccesses may affect validity of array
// copy stubs and nmethods
if (!test_aarch64_flag(aarch64_avoidUnalignedAccesses) && AvoidUnalignedAccesses) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with AvoidUnalignedAccesses = false vs current = true");
return false;
}
// change to UseSIMDForMemoryOps may affect validity of array
// copy stubs and nmethods
CHECK_FLAG(aarch64_, aarch64_useSIMDForMemoryOps, UseSIMDForMemoryOps);
// change to UseSIMDForArrayEquals may affect validity of array
// copy stubs and nmethods
CHECK_FLAG(aarch64_, aarch64_useSIMDForArrayEquals, UseSIMDForArrayEquals);
// change to useSIMDForSHA3 may affect validity of SHA3 stubs
CHECK_FLAG(aarch64_, aarch64_useSIMDForSHA3, UseSIMDForSHA3Intrinsic);
// change to UseLSE may affect validity of stubs and nmethods
CHECK_FLAG(aarch64_, aarch64_useLSE, UseLSE);
// check aarch64-specific intrinsic use settings are compatible
CHECK_FLAG(aarch64_use_, aarch64_useBlockZeroing, UseBlockZeroing);
CHECK_FLAG(aarch64_use_, aarch64_useSIMDForBigIntegerShift, UseSIMDForBigIntegerShiftIntrinsics);
CHECK_FLAG(aarch64_use_, aarch64_useSimpleArrayEquals, UseSimpleArrayEquals);
CHECK_FLAG(aarch64_use_, aarch64_useSecondarySupersCache, UseSecondarySupersCache);
#endif // defined(AARCH64) && !defined(ZERO)
#if INCLUDE_JVMCI
// change to EnableJVMCI will affect validity of adapters and
// nmethods
if (_enableJVMCI != (uint)EnableJVMCI) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with EnableJVMCI = %s vs current %s", (_enableJVMCI ? "true" : "false"), (EnableJVMCI ? "true" : "false"));
return false;
}
#endif // INCLUDE_JVMCI
// The following checks do not affect AOT adapters caching
if (((_flags & compressedOops) != 0) != UseCompressedOops) {
if (test_flag(compressedOops) != UseCompressedOops) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedOops = %s", UseCompressedOops ? "false" : "true");
AOTStubCaching = false;
}
@ -493,12 +764,11 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
AOTStubCaching = false;
}
if (!verify_cpu_features(cache)) {
return false;
}
return true;
}
#undef TEST_FLAG
bool AOTCodeCache::Header::verify(uint load_size) const {
if (_version != AOT_CODE_VERSION) {
log_debug(aot, codecache, init)("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version);

View File

@ -173,6 +173,16 @@ protected:
uint _compressedKlassShift;
uint _contendedPaddingWidth;
uint _gc;
uint _optoLoopAlignment;
uint _codeEntryAlignment;
uint _allocatePrefetchLines;
uint _allocateInstancePrefetchLines;
uint _allocatePrefetchDistance;
uint _allocatePrefetchStepSize;
#ifdef COMPILER2
uint _maxVectorSize;
uint _arrayOperationPartialInlineSize;
#endif // COMPILER2
enum Flags {
none = 0,
debugVM = 1,
@ -184,8 +194,90 @@ protected:
restrictContendedPadding = 64
};
uint _flags;
enum IntrinsicsUseFlags {
use_none = 0,
useCRC32 = 1 << 0,
useCRC32C = 1 << 1,
useMultiplyToLen = 1 << 2,
useSquareToLen = 1 << 3,
useMulAdd = 1 << 4,
useMontgomeryMultiply = 1 << 5,
useMontgomerySquare = 1 << 6,
useChaCha20 = 1 << 7,
useDilithium = 1 << 8,
useKyber = 1 << 9,
useBASE64 = 1 << 10,
useAdler32 = 1 << 11,
useAES = 1 << 12,
useAESCTR = 1 << 13,
useGHASH = 1 << 14,
useMD5 = 1 << 15,
useSHA1 = 1 << 16,
useSHA256 = 1 << 17,
useSHA512 = 1 << 18,
useSHA3 = 1 << 19,
usePoly1305 = 1 << 20,
useVectorizedMismatch = 1 << 21,
useSecondarySupersTable = 1 << 22,
};
uint _use_intrinsics_flags;
bool test_flag(enum Flags flag) const { return (_flags & flag) != 0; }
bool test_use_flag(enum IntrinsicsUseFlags flag) const { return (_use_intrinsics_flags & flag) != 0; }
void set_flag(enum Flags flag) { _flags |= flag; }
void set_use_flag(enum IntrinsicsUseFlags flag) { _use_intrinsics_flags |= flag; }
#if defined(X86) && !defined(ZERO)
uint _avx3threshold;
uint _useAVX;
enum X86Flags {
x86_none = 0,
x86_enableX86ECoreOpts = 1,
x86_useUnalignedLoadStores = 2,
x86_useAPX = 4
};
uint _x86_flags;
enum X86IntrinsicsUseFlags {
x86_use_none = 0,
x86_useLibm = 1 << 1,
x86_useIntPoly = 1 << 2,
};
uint _x86_use_intrinsics_flags;
bool test_x86_flag(enum X86Flags flag) const { return (_x86_flags & flag) != 0; }
bool test_x86_use_flag(enum X86IntrinsicsUseFlags flag) const { return (_x86_use_intrinsics_flags & flag) != 0; }
void set_x86_flag(enum X86Flags flag) { _x86_flags |= flag; }
void set_x86_use_flag(enum X86IntrinsicsUseFlags flag) { _x86_use_intrinsics_flags |= flag; }
#endif // defined(X86) && !defined(ZERO)
#if defined(AARCH64) && !defined(ZERO)
// this is global but x86 does not use it and aarch64 does
uint _prefetchCopyIntervalInBytes;
uint _blockZeroingLowLimit;
uint _softwarePrefetchHintDistance;
uint _useSVE;
enum AArch64Flags {
aarch64_none = 0,
aarch64_avoidUnalignedAccesses = 1,
aarch64_useSIMDForMemoryOps = 2,
aarch64_useSIMDForArrayEquals = 4,
aarch64_useSIMDForSHA3 = 8,
aarch64_useLSE = 16,
};
uint _aarch64_flags;
enum AArch64IntrinsicsUseFlags {
aarch64_use_none = 0,
aarch64_useBlockZeroing = 1 << 0,
aarch64_useSIMDForBigIntegerShift = 1 << 1,
aarch64_useSimpleArrayEquals = 1 << 2,
aarch64_useSecondarySupersCache = 1 << 3,
};
uint _aarch64_use_intrinsics_flags;
bool test_aarch64_flag(enum AArch64Flags flag) const { return (_aarch64_flags & flag) != 0; }
bool test_aarch64_use_flag(enum AArch64IntrinsicsUseFlags flag) const { return (_aarch64_use_intrinsics_flags & flag) != 0; }
void set_aarch64_flag(enum AArch64Flags flag) { _aarch64_flags |= flag; }
void set_aarch64_use_flag(enum AArch64IntrinsicsUseFlags flag) { _aarch64_use_intrinsics_flags |= flag; }
#endif // defined(AARCH64) && !defined(ZERO)
#if INCLUDE_JVMCI
uint _enableJVMCI;
#endif // INCLUDE_JVMCI
uint _cpu_features_offset; // offset in the cache where cpu features are stored
public:
void record(uint cpu_features_offset);
bool verify_cpu_features(AOTCodeCache* cache) const;