Skip to content

Commit 8f66fee

Browse files
committed
Lots more tests and feature roundtripping
1 parent ae86870 commit 8f66fee

File tree

3 files changed

+180
-2
lines changed

3 files changed

+180
-2
lines changed

include/archspec/llvm_compat.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ class Microarchitecture;
2525
*/
2626
std::string map_feature_to_llvm(std::string_view arch_family, std::string_view feature);
2727

28+
/**
29+
* Map an LLVM feature name back to archspec feature name.
30+
* Reverses the mapping done by map_feature_to_llvm.
31+
* Returns the input unchanged if no mapping is needed.
32+
*
33+
* @param arch_family Architecture family ("x86_64", "aarch64", etc.)
34+
* @param feature The LLVM feature name
35+
* @return archspec-compatible feature name
36+
*/
37+
std::string map_llvm_feature_to_archspec(std::string_view arch_family, std::string_view feature);
38+
2839
/**
2940
* Get all LLVM-compatible features for a microarchitecture.
3041
* Handles mapping and filtering automatically.

src/llvm_compat.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,49 @@ std::string map_feature_to_llvm(std::string_view arch_family, std::string_view f
181181
return feat_str;
182182
}
183183

184+
std::string map_llvm_feature_to_archspec(std::string_view arch_family, std::string_view feature) {
185+
std::string feat_str(feature);
186+
187+
// Build reverse maps lazily (static)
188+
static std::unordered_map<std::string, std::string> aarch64_reverse_map;
189+
static std::unordered_map<std::string, std::string> x86_64_reverse_map;
190+
static std::unordered_map<std::string, std::string> riscv_reverse_map;
191+
static bool initialized = false;
192+
193+
if (!initialized) {
194+
for (const auto& p : aarch64_feature_map) {
195+
aarch64_reverse_map[p.second] = p.first;
196+
}
197+
for (const auto& p : x86_64_feature_map) {
198+
x86_64_reverse_map[p.second] = p.first;
199+
}
200+
for (const auto& p : riscv_feature_map) {
201+
riscv_reverse_map[p.second] = p.first;
202+
}
203+
initialized = true;
204+
}
205+
206+
if (arch_family == "aarch64") {
207+
auto it = aarch64_reverse_map.find(feat_str);
208+
if (it != aarch64_reverse_map.end()) {
209+
return it->second;
210+
}
211+
} else if (arch_family == "x86_64" || arch_family == "x86") {
212+
auto it = x86_64_reverse_map.find(feat_str);
213+
if (it != x86_64_reverse_map.end()) {
214+
return it->second;
215+
}
216+
} else if (arch_family == "riscv64" || arch_family == "riscv32") {
217+
auto it = riscv_reverse_map.find(feat_str);
218+
if (it != riscv_reverse_map.end()) {
219+
return it->second;
220+
}
221+
}
222+
223+
// No mapping needed, return as-is
224+
return feat_str;
225+
}
226+
184227
std::set<std::string> get_llvm_features(const Microarchitecture& uarch) {
185228
std::set<std::string> result;
186229
std::string family = uarch.family();
@@ -300,12 +343,18 @@ static const std::unordered_map<std::string, std::string> x86_64_cpu_reverse_map
300343
{"znver2", "zen2"},
301344
{"znver3", "zen3"},
302345
{"znver4", "zen4"},
346+
{"znver5", "zen5"},
303347
// Intel
304348
{"icelake-client", "icelake"},
305349
{"icelake-server", "icelake_server"},
306350
{"skylake-avx512", "skylake_avx512"},
307351
{"cascadelake", "cascadelake"},
308352
{"cooperlake", "cooperlake"},
353+
// x86-64 feature levels
354+
{"x86-64", "x86_64"},
355+
{"x86-64-v2", "x86_64_v2"},
356+
{"x86-64-v3", "x86_64_v3"},
357+
{"x86-64-v4", "x86_64_v4"},
309358
};
310359

311360
std::string normalize_cpu_name(std::string_view arch_family, std::string_view llvm_name) {

tests/test_llvm_compat.cpp

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,23 +131,139 @@ void test_host_llvm_features() {
131131
printf(" PASS: host LLVM features\n");
132132
}
133133

134+
void test_llvm_to_archspec_feature_mapping() {
135+
printf("Testing LLVM to archspec feature mapping (reverse)...\n");
136+
137+
// Test x86_64 reverse mappings (LLVM -> archspec)
138+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "sse4.1") == "sse4_1");
139+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "sse4.2") == "sse4_2");
140+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "avx512vnni") == "avx512_vnni");
141+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "avx512bf16") == "avx512_bf16");
142+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "sahf") == "lahf_lm");
143+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "pclmul") == "pclmulqdq");
144+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "rdrnd") == "rdrand");
145+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "lzcnt") == "abm");
146+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "bmi") == "bmi1");
147+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "sha") == "sha_ni");
148+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "amx-bf16") == "amx_bf16");
149+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "amx-int8") == "amx_int8");
150+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "amx-tile") == "amx_tile");
151+
152+
// Test AArch64 reverse mappings
153+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "neon") == "asimd");
154+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "dotprod") == "asimddp");
155+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "crc") == "crc32");
156+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "lse") == "atomics");
157+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "complxnum") == "fcma");
158+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "rcpc") == "lrcpc");
159+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "pauth") == "paca");
160+
161+
// Test pass-through (features that don't need mapping)
162+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "avx") == "avx");
163+
assert(archspec::map_llvm_feature_to_archspec("x86_64", "avx2") == "avx2");
164+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "aes") == "aes");
165+
assert(archspec::map_llvm_feature_to_archspec("aarch64", "sha2") == "sha2");
166+
167+
printf(" PASS: LLVM to archspec feature mapping\n");
168+
}
169+
170+
void test_feature_roundtrip() {
171+
printf("Testing feature mapping roundtrip...\n");
172+
173+
// Test that archspec -> LLVM -> archspec roundtrips correctly for x86_64
174+
auto test_x86_roundtrip = [](const char* archspec_feat) {
175+
std::string llvm = archspec::map_feature_to_llvm("x86_64", archspec_feat);
176+
if (!llvm.empty()) {
177+
std::string back = archspec::map_llvm_feature_to_archspec("x86_64", llvm);
178+
if (back != archspec_feat) {
179+
printf(" FAIL: %s -> %s -> %s\n", archspec_feat, llvm.c_str(), back.c_str());
180+
assert(false);
181+
}
182+
}
183+
};
184+
185+
test_x86_roundtrip("sse4_1");
186+
test_x86_roundtrip("sse4_2");
187+
test_x86_roundtrip("rdrand");
188+
test_x86_roundtrip("lahf_lm");
189+
test_x86_roundtrip("pclmulqdq");
190+
test_x86_roundtrip("bmi1");
191+
test_x86_roundtrip("sha_ni");
192+
test_x86_roundtrip("amx_bf16");
193+
194+
// Test AArch64 roundtrip
195+
auto test_aarch64_roundtrip = [](const char* archspec_feat) {
196+
std::string llvm = archspec::map_feature_to_llvm("aarch64", archspec_feat);
197+
if (!llvm.empty()) {
198+
std::string back = archspec::map_llvm_feature_to_archspec("aarch64", llvm);
199+
if (back != archspec_feat) {
200+
printf(" FAIL: %s -> %s -> %s\n", archspec_feat, llvm.c_str(), back.c_str());
201+
assert(false);
202+
}
203+
}
204+
};
205+
206+
test_aarch64_roundtrip("asimd");
207+
test_aarch64_roundtrip("asimddp");
208+
test_aarch64_roundtrip("crc32");
209+
test_aarch64_roundtrip("atomics");
210+
test_aarch64_roundtrip("fcma");
211+
test_aarch64_roundtrip("lrcpc");
212+
test_aarch64_roundtrip("paca");
213+
214+
printf(" PASS: feature mapping roundtrip\n");
215+
}
216+
134217
void test_cpu_name_normalization() {
135218
printf("Testing CPU name normalization...\n");
136219

137-
// Test AArch64 reverse mappings
138-
assert(archspec::normalize_cpu_name("aarch64", "apple-m4") == "m4");
220+
// Test Apple Silicon normalization
139221
assert(archspec::normalize_cpu_name("aarch64", "apple-m1") == "m1");
222+
assert(archspec::normalize_cpu_name("aarch64", "apple-m2") == "m2");
223+
assert(archspec::normalize_cpu_name("aarch64", "apple-m3") == "m3");
224+
assert(archspec::normalize_cpu_name("aarch64", "apple-m4") == "m4");
140225
assert(archspec::normalize_cpu_name("aarch64", "apple-a15") == "a15");
226+
assert(archspec::normalize_cpu_name("aarch64", "apple-a16") == "a16");
227+
228+
// Test ARM Cortex normalization (only cortex_a72 is in archspec DB)
229+
assert(archspec::normalize_cpu_name("aarch64", "cortex-a57") == "aarch64");
141230
assert(archspec::normalize_cpu_name("aarch64", "cortex-a72") == "cortex_a72");
231+
assert(archspec::normalize_cpu_name("aarch64", "cortex-a73") == "cortex_a72");
232+
assert(archspec::normalize_cpu_name("aarch64", "cortex-a76") == "cortex_a72");
233+
234+
// Test Neoverse normalization
142235
assert(archspec::normalize_cpu_name("aarch64", "neoverse-n1") == "neoverse_n1");
236+
assert(archspec::normalize_cpu_name("aarch64", "neoverse-n2") == "neoverse_n2");
237+
assert(archspec::normalize_cpu_name("aarch64", "neoverse-v1") == "neoverse_v1");
238+
assert(archspec::normalize_cpu_name("aarch64", "neoverse-v2") == "neoverse_v2");
239+
240+
// Test Thunderx normalization
241+
assert(archspec::normalize_cpu_name("aarch64", "thunderx2t99") == "thunderx2");
242+
assert(archspec::normalize_cpu_name("aarch64", "thunderx3t110") == "thunderx2");
243+
244+
// Test Nvidia/Ampere normalization
245+
assert(archspec::normalize_cpu_name("aarch64", "carmel") == "aarch64");
246+
assert(archspec::normalize_cpu_name("aarch64", "ampere1") == "neoverse_n1");
247+
assert(archspec::normalize_cpu_name("aarch64", "ampere1a") == "neoverse_n1");
143248

144249
// Test x86 reverse mappings
145250
assert(archspec::normalize_cpu_name("x86_64", "znver3") == "zen3");
146251
assert(archspec::normalize_cpu_name("x86_64", "znver4") == "zen4");
252+
assert(archspec::normalize_cpu_name("x86_64", "znver5") == "zen5");
147253
assert(archspec::normalize_cpu_name("x86_64", "icelake-client") == "icelake");
254+
assert(archspec::normalize_cpu_name("x86_64", "icelake-server") == "icelake_server");
255+
assert(archspec::normalize_cpu_name("x86_64", "skylake-avx512") == "skylake_avx512");
256+
assert(archspec::normalize_cpu_name("x86_64", "cascadelake") == "cascadelake");
257+
258+
// Test x86-64 feature levels
259+
assert(archspec::normalize_cpu_name("x86_64", "x86-64") == "x86_64");
260+
assert(archspec::normalize_cpu_name("x86_64", "x86-64-v2") == "x86_64_v2");
261+
assert(archspec::normalize_cpu_name("x86_64", "x86-64-v3") == "x86_64_v3");
262+
assert(archspec::normalize_cpu_name("x86_64", "x86-64-v4") == "x86_64_v4");
148263

149264
// Test pass-through
150265
assert(archspec::normalize_cpu_name("x86_64", "haswell") == "haswell");
266+
assert(archspec::normalize_cpu_name("x86_64", "broadwell") == "broadwell");
151267
assert(archspec::normalize_cpu_name("aarch64", "generic") == "generic");
152268

153269
printf(" PASS: CPU name normalization\n");
@@ -190,6 +306,8 @@ int main() {
190306

191307
test_aarch64_feature_mapping();
192308
test_x86_feature_mapping();
309+
test_llvm_to_archspec_feature_mapping();
310+
test_feature_roundtrip();
193311
test_cpu_name_mapping();
194312
test_host_llvm_features();
195313
test_cpu_name_normalization();

0 commit comments

Comments
 (0)