Skip to content

Commit 46bd115

Browse files
authored
Replace __cpuid_check for AVX512 with compiler builtin cpu features (#655)
Replaces manual CPUID bit parsing in jvector_simd_check.c with __builtin_cpu_supports and adds __builtin_cpu_init(). This corrects missing XSAVE runtime checks, preventing #UD exceptions on systems where 512‑bit register state saves are disabled.
1 parent c852384 commit 46bd115

6 files changed

Lines changed: 91 additions & 36 deletions

File tree

jvector-native/src/main/c/jvector_simd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ __m512i maskEighthBit;
2626

2727
__attribute__((constructor))
2828
void initialize_constants() {
29-
if (check_compatibility()) {
29+
if (check_avx512_compatibility()) {
3030
initialIndexRegister = _mm512_setr_epi32(-16, -15, -14, -13, -12, -11, -10, -9,
3131
-8, -7, -6, -5, -4, -3, -2, -1);
3232
indexIncrement = _mm512_set1_epi32(16);

jvector-native/src/main/c/jvector_simd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#define VECTOR_SIMD_DOT_H
2121

2222
// check CPU support
23-
bool check_compatibility(void);
23+
bool check_avx512_compatibility(void);
2424

2525
//F32
2626
float dot_product_f32(int preferred_size, const float* a, int aoffset, const float* b, int boffset, int length);

jvector-native/src/main/c/jvector_simd_check.c

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,14 @@
1717
#include <cpuid.h>
1818
#include "jvector_simd.h"
1919

20-
bool check_compatibility(void) {
21-
unsigned int eax, ebx, ecx, edx;
22-
bool avx512f_supported = false, avx512cd_supported = false,
23-
avx512bw_supported = false, avx512dq_supported = false,
24-
avx512vl_supported = false;
25-
26-
// Check for AVX-512 Foundation (AVX-512F) and other AVX-512 features:
27-
// These are indicated by various bits of EBX from leaf 7, sub-leaf 0.
28-
if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) {
29-
avx512f_supported = ebx & (1 << 16); // AVX-512F
30-
avx512cd_supported = ebx & (1 << 28); // AVX-512CD
31-
avx512bw_supported = ebx & (1 << 30); // AVX-512BW
32-
avx512dq_supported = ebx & (1 << 17); // AVX-512DQ
33-
avx512vl_supported = ebx & (1 << 31); // AVX-512VL
34-
}
35-
36-
return avx512f_supported && avx512cd_supported && avx512bw_supported && avx512dq_supported && avx512vl_supported;
37-
}
20+
bool check_avx512_compatibility(void) {
21+
/* __builtin_cpu_init required when this is used in ifunc
22+
resolver/__attribute__((constructor)) context, otherwise the CPU
23+
features may not be detected correctly. */
24+
__builtin_cpu_init();
25+
return (__builtin_cpu_supports("avx512f") &&
26+
__builtin_cpu_supports("avx512cd") &&
27+
__builtin_cpu_supports("avx512dq") &&
28+
__builtin_cpu_supports("avx512bw") &&
29+
__builtin_cpu_supports("avx512vl"));
30+
}

jvector-native/src/main/java/io/github/jbellis/jvector/vector/NativeVectorizationProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public NativeVectorizationProvider() {
3535
if (!libraryLoaded) {
3636
throw new UnsupportedOperationException("Failed to load supporting native library.");
3737
}
38-
if (!NativeSimdOps.check_compatibility()) {
38+
if (!NativeSimdOps.check_avx512_compatibility()) {
3939
throw new UnsupportedOperationException("Native SIMD operations are not supported on this platform due to missing CPU support.");
4040
}
4141
this.vectorUtilSupport = new NativeVectorUtilSupport();

jvector-native/src/main/java/io/github/jbellis/jvector/vector/cnative/NativeSimdOps.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,55 +96,55 @@ public static int __bool_true_false_are_defined() {
9696
return __bool_true_false_are_defined;
9797
}
9898

99-
private static class check_compatibility {
99+
private static class check_avx512_compatibility {
100100
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
101101
NativeSimdOps.C_BOOL );
102102

103-
public static final MemorySegment ADDR = NativeSimdOps.findOrThrow("check_compatibility");
103+
public static final MemorySegment ADDR = NativeSimdOps.findOrThrow("check_avx512_compatibility");
104104

105105
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC, Linker.Option.critical(true));
106106
}
107107

108108
/**
109109
* Function descriptor for:
110110
* {@snippet lang=c :
111-
* _Bool check_compatibility()
111+
* _Bool check_avx512_compatibility()
112112
* }
113113
*/
114-
public static FunctionDescriptor check_compatibility$descriptor() {
115-
return check_compatibility.DESC;
114+
public static FunctionDescriptor check_avx512_compatibility$descriptor() {
115+
return check_avx512_compatibility.DESC;
116116
}
117117

118118
/**
119119
* Downcall method handle for:
120120
* {@snippet lang=c :
121-
* _Bool check_compatibility()
121+
* _Bool check_avx512_compatibility()
122122
* }
123123
*/
124-
public static MethodHandle check_compatibility$handle() {
125-
return check_compatibility.HANDLE;
124+
public static MethodHandle check_avx512_compatibility$handle() {
125+
return check_avx512_compatibility.HANDLE;
126126
}
127127

128128
/**
129129
* Address for:
130130
* {@snippet lang=c :
131-
* _Bool check_compatibility()
131+
* _Bool check_avx512_compatibility()
132132
* }
133133
*/
134-
public static MemorySegment check_compatibility$address() {
135-
return check_compatibility.ADDR;
134+
public static MemorySegment check_avx512_compatibility$address() {
135+
return check_avx512_compatibility.ADDR;
136136
}
137137

138138
/**
139139
* {@snippet lang=c :
140-
* _Bool check_compatibility()
140+
* _Bool check_avx512_compatibility()
141141
* }
142142
*/
143-
public static boolean check_compatibility() {
144-
var mh$ = check_compatibility.HANDLE;
143+
public static boolean check_avx512_compatibility() {
144+
var mh$ = check_avx512_compatibility.HANDLE;
145145
try {
146146
if (TRACE_DOWNCALLS) {
147-
traceDowncall("check_compatibility");
147+
traceDowncall("check_avx512_compatibility");
148148
}
149149
return (boolean)mh$.invokeExact();
150150
} catch (Throwable ex$) {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.github.jbellis.jvector.vector.cnative;
18+
19+
import org.junit.Assume;
20+
import org.junit.Test;
21+
22+
import java.io.IOException;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
25+
import java.util.List;
26+
27+
import static org.junit.Assert.assertEquals;
28+
29+
public class NativeSimdOpsTest {
30+
31+
/**
32+
* Reads /proc/cpuinfo and returns true if all AVX-512 flags required by
33+
* check_avx512_compatibility() are present: avx512f, avx512cd, avx512dq,
34+
* avx512bw, avx512vl.
35+
*/
36+
private static boolean cpuinfoReportsAvx512() throws IOException {
37+
List<String> lines = Files.readAllLines(Path.of("/proc/cpuinfo"));
38+
List<String> required = List.of("avx512f", "avx512cd", "avx512dq", "avx512bw", "avx512vl");
39+
for (String line : lines) {
40+
if (line.startsWith("flags")) {
41+
String[] flags = line.split("\\s+");
42+
List<String> flagList = List.of(flags);
43+
return flagList.containsAll(required);
44+
}
45+
}
46+
return false;
47+
}
48+
49+
@Test
50+
public void testCheckAvx512CompatibilityMatchesCpuinfo() throws IOException {
51+
boolean libraryLoaded = LibraryLoader.loadJvector();
52+
Assume.assumeTrue("Native jvector library not available; skipping AVX-512 check", libraryLoaded);
53+
54+
boolean expectedFromCpuinfo = cpuinfoReportsAvx512();
55+
boolean actualFromNative = NativeSimdOps.check_avx512_compatibility();
56+
57+
assertEquals(
58+
"check_avx512_compatibility() should match AVX-512 flag presence in /proc/cpuinfo",
59+
expectedFromCpuinfo,
60+
actualFromNative);
61+
}
62+
}

0 commit comments

Comments
 (0)