target_test.c (16470B)
1 /* target_test - public <kit/core.h> target creation and feature API. */ 2 3 #include <kit/core.h> 4 #include <kit/os.h> 5 #include <kit/target.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "lib/kit_unit.h" 10 11 static KitUnit g_u; 12 #define EXPECT(c, ...) CU_EXPECT(&g_u, c, __VA_ARGS__) 13 14 static KitTargetSpec target_spec(KitArchKind arch, KitOSKind os, 15 KitObjFmt obj) { 16 KitTargetSpec t = kit_unit_target(arch, os, obj); 17 if (arch == KIT_ARCH_WASM) { 18 t.ptr_size = 4; 19 t.ptr_align = 4; 20 } 21 return t; 22 } 23 24 static KitStatus make_target(KitTargetSpec spec, KitSlice isa, 25 const KitTargetFeature* features, 26 uint32_t nfeatures, KitTarget** out) { 27 KitTargetOptions opts; 28 memset(&opts, 0, sizeof opts); 29 opts.spec = spec; 30 opts.isa = isa; 31 opts.features = features; 32 opts.nfeatures = nfeatures; 33 return kit_target_new(&g_u.ctx, &opts, out); 34 } 35 36 static int has(KitTarget* t, const char* name) { 37 return kit_target_has_feature(t, kit_slice_cstr(name)); 38 } 39 40 static void check_target_triple_parse(void) { 41 KitTargetSpec t; 42 KitArchKind arch = KIT_ARCH_X86_32; 43 uint8_t ptr_size = 0; 44 45 EXPECT(kit_arch_from_name("riscv64gc", &arch, &ptr_size), 46 "riscv64gc arch spelling accepted"); 47 EXPECT(arch == KIT_ARCH_RV64 && ptr_size == 8, 48 "riscv64gc maps to rv64"); 49 50 EXPECT(kit_arch_from_name("riscv32imac", &arch, &ptr_size), 51 "riscv32imac arch spelling accepted"); 52 EXPECT(arch == KIT_ARCH_RV32 && ptr_size == 4, 53 "riscv32imac maps to rv32"); 54 55 memset(&t, 0, sizeof t); 56 EXPECT(kit_target_from_triple("riscv64gc-unknown-linux-gnu", &t), 57 "Rust riscv64gc Linux triple parses"); 58 EXPECT(t.arch == KIT_ARCH_RV64 && t.os == KIT_OS_LINUX && 59 t.obj == KIT_OBJ_ELF, 60 "Rust riscv64gc Linux triple maps to ELF Linux rv64"); 61 62 memset(&t, 0, sizeof t); 63 EXPECT(kit_target_from_triple("riscv32imac-unknown-none-elf", &t), 64 "Rust riscv32imac none triple parses"); 65 EXPECT(t.arch == KIT_ARCH_RV32 && t.os == KIT_OS_FREESTANDING && 66 t.obj == KIT_OBJ_ELF, 67 "Rust riscv32imac none triple maps to freestanding rv32"); 68 69 memset(&t, 0, sizeof t); 70 EXPECT(kit_target_from_triple("arm64-apple-ios17.0", &t), 71 "arm64 iOS triple parses"); 72 EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_IOS && 73 t.obj == KIT_OBJ_MACHO, 74 "arm64 iOS triple maps to Mach-O iOS"); 75 76 memset(&t, 0, sizeof t); 77 EXPECT(kit_target_from_triple("arm64-apple-ios17.0-simulator", &t), 78 "arm64 iOS simulator triple parses"); 79 EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_IOS_SIMULATOR && 80 t.obj == KIT_OBJ_MACHO, 81 "arm64 iOS simulator triple maps to Mach-O iOS simulator"); 82 83 { 84 char buf[64]; 85 EXPECT(kit_target_to_triple(t, buf, sizeof buf), 86 "iOS simulator target renders"); 87 EXPECT(strcmp(buf, "aarch64-apple-ios-simulator") == 0, 88 "iOS simulator target renders canonical triple"); 89 } 90 91 memset(&t, 0, sizeof t); 92 EXPECT(kit_target_from_triple("aarch64-linux-android24", &t), 93 "aarch64 Android API triple parses"); 94 EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_ANDROID && 95 t.obj == KIT_OBJ_ELF && t.os_version_major == 24, 96 "aarch64 Android API triple maps to ELF Android"); 97 98 { 99 char buf[64]; 100 EXPECT(kit_target_to_triple(t, buf, sizeof buf), 101 "Android target renders"); 102 EXPECT(strcmp(buf, "aarch64-linux-android24") == 0, 103 "Android target renders canonical API triple"); 104 } 105 106 /* arm/armv7 both map to KIT_ARCH_ARM_32; the canonical triple spelling is 107 * "arm" (distinct from the "arm32" diagnostic name). */ 108 EXPECT(kit_arch_from_name("arm", &arch, &ptr_size) && 109 arch == KIT_ARCH_ARM_32 && ptr_size == 4, 110 "arm arch spelling maps to arm32"); 111 EXPECT(kit_arch_from_name("armv7", &arch, &ptr_size) && 112 arch == KIT_ARCH_ARM_32 && ptr_size == 4, 113 "armv7 arch spelling maps to arm32"); 114 115 memset(&t, 0, sizeof t); 116 EXPECT(kit_target_from_triple("armv7-unknown-linux-gnueabihf", &t), 117 "armv7 Linux triple parses"); 118 EXPECT(t.arch == KIT_ARCH_ARM_32 && t.os == KIT_OS_LINUX && 119 t.obj == KIT_OBJ_ELF, 120 "armv7 Linux triple maps to ELF Linux arm32"); 121 { 122 char buf[64]; 123 EXPECT(kit_target_to_triple(t, buf, sizeof buf), "arm32 target renders"); 124 EXPECT(strcmp(buf, "arm-linux") == 0, 125 "arm32 target renders canonical \"arm\" component"); 126 } 127 128 /* Plain (non-simulator) iOS round-trips to "aarch64-apple-ios". */ 129 memset(&t, 0, sizeof t); 130 EXPECT(kit_target_from_triple("arm64-apple-ios", &t), "arm64 iOS bare parses"); 131 EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_IOS && 132 t.obj == KIT_OBJ_MACHO, 133 "arm64 iOS bare maps to Mach-O iOS"); 134 { 135 char buf[64]; 136 EXPECT(kit_target_to_triple(t, buf, sizeof buf), "iOS target renders"); 137 EXPECT(strcmp(buf, "aarch64-apple-ios") == 0, 138 "iOS target renders canonical triple"); 139 } 140 141 /* iphonesimulator look-ahead form resolves to the simulator OS + Mach-O. */ 142 memset(&t, 0, sizeof t); 143 EXPECT(kit_target_from_triple("arm64-apple-ios-simulator", &t), 144 "arm64 iOS simulator (no version) parses"); 145 EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_IOS_SIMULATOR && 146 t.obj == KIT_OBJ_MACHO, 147 "arm64 iOS simulator (no version) maps to Mach-O iOS simulator"); 148 149 /* Bare android env component (no preceding linux token) parses to ELF. */ 150 memset(&t, 0, sizeof t); 151 EXPECT(kit_target_from_triple("aarch64-android", &t), 152 "aarch64 bare android triple parses"); 153 EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_ANDROID && 154 t.obj == KIT_OBJ_ELF && t.os_version_major == 0, 155 "aarch64 bare android maps to ELF Android, no API level"); 156 { 157 char buf[64]; 158 EXPECT(kit_target_to_triple(t, buf, sizeof buf), 159 "Android (no API) target renders"); 160 EXPECT(strcmp(buf, "aarch64-linux-android") == 0, 161 "Android (no API) renders canonical triple without suffix"); 162 } 163 164 /* Diagnostic names: distinct from the triple spelling for arm. */ 165 EXPECT(strcmp(kit_target_arch_diag_name(KIT_ARCH_ARM_32), "arm32") == 0, 166 "arm32 diag name"); 167 EXPECT(strcmp(kit_target_arch_diag_name(KIT_ARCH_ARM_64), "aarch64") == 0, 168 "aarch64 diag name"); 169 EXPECT(strcmp(kit_target_arch_diag_name(KIT_ARCH_X86_32), "x86_32") == 0, 170 "x86_32 diag name"); 171 EXPECT(strcmp(kit_target_arch_diag_name(KIT_ARCH_WASM), "wasm") == 0, 172 "wasm diag name"); 173 } 174 175 static void check_x64_defaults_and_isa(void) { 176 KitTargetSpec spec = target_spec(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); 177 KitTargetFeature disable_avx2[] = { 178 {KIT_SLICE_LIT("avx2"), false}, 179 }; 180 KitTarget* t = NULL; 181 182 EXPECT(make_target(spec, KIT_SLICE_NULL, NULL, 0, &t) == KIT_OK, 183 "x64 default target"); 184 EXPECT(has(t, "sse"), "x64 default has sse"); 185 EXPECT(has(t, "sse2"), "x64 default has sse2"); 186 EXPECT(!has(t, "avx"), "x64 default lacks avx"); 187 EXPECT(!has(t, "popcnt"), "x64 default lacks popcnt"); 188 kit_target_free(t); 189 190 t = NULL; 191 EXPECT(make_target(spec, KIT_SLICE_LIT("x86-64-v2"), NULL, 0, &t) == KIT_OK, 192 "x64 v2 target"); 193 EXPECT(has(t, "popcnt"), "x86-64-v2 enables popcnt"); 194 EXPECT(!has(t, "avx"), "x86-64-v2 lacks avx"); 195 kit_target_free(t); 196 197 t = NULL; 198 EXPECT(make_target(spec, KIT_SLICE_LIT("x86-64-v3"), disable_avx2, 1, &t) == 199 KIT_OK, 200 "x64 isa profile plus feature override"); 201 EXPECT(has(t, "avx"), "x86-64-v3 enables avx"); 202 EXPECT(has(t, "popcnt"), "x86-64-v3 enables popcnt"); 203 EXPECT(!has(t, "avx2"), "explicit feature override disables avx2"); 204 kit_target_free(t); 205 } 206 207 static void check_rv64_isa_and_overrides(void) { 208 KitTargetSpec spec = target_spec(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); 209 KitTargetFeature enable_c[] = { 210 {KIT_SLICE_LIT("c"), true}, 211 }; 212 KitTarget* t = NULL; 213 214 EXPECT(make_target(spec, KIT_SLICE_LIT("rv64im"), enable_c, 1, &t) == KIT_OK, 215 "rv64 isa plus feature override"); 216 EXPECT(has(t, "i"), "rv64im has i"); 217 EXPECT(has(t, "m"), "rv64im has m"); 218 EXPECT(!has(t, "a"), "rv64im lacks a"); 219 EXPECT(has(t, "c"), "explicit feature override enables c"); 220 EXPECT(!has(t, "zicsr"), "rv64im lacks zicsr"); 221 kit_target_free(t); 222 } 223 224 static void check_wasm_features(void) { 225 KitTargetSpec spec = target_spec(KIT_ARCH_WASM, KIT_OS_WASI, KIT_OBJ_WASM); 226 KitTargetSpec parsed; 227 KitTargetFeature disable_tail_calls[] = { 228 {KIT_SLICE_LIT("tail-calls"), false}, 229 }; 230 KitTarget* t = NULL; 231 KitTargetSpec resolved; 232 233 EXPECT(make_target(spec, KIT_SLICE_LIT("wasm32"), disable_tail_calls, 1, 234 &t) == KIT_OK, 235 "wasm32 target with disabled tail-calls"); 236 resolved = kit_target_spec(t); 237 EXPECT(resolved.arch == KIT_ARCH_WASM && resolved.ptr_size == 4, 238 "kit_target_spec preserves wasm32 shape"); 239 EXPECT(!has(t, "tail-calls"), 240 "explicit feature override disables tail-calls"); 241 EXPECT(has(t, "bulk-memory"), "wasm default still has bulk-memory"); 242 EXPECT(!has(t, "not-a-feature"), "unknown queried feature is absent"); 243 kit_target_free(t); 244 245 memset(&parsed, 0, sizeof parsed); 246 EXPECT(kit_target_from_triple("wasm64-wasi", &parsed), 247 "wasm64 triple remains parseable as a reserved spelling"); 248 EXPECT(parsed.arch == KIT_ARCH_WASM && parsed.ptr_size == 8, 249 "wasm64 triple carries 8-byte pointer size"); 250 g_u.last_diag[0] = '\0'; 251 EXPECT(make_target(parsed, KIT_SLICE_NULL, NULL, 0, &t) == KIT_UNSUPPORTED, 252 "wasm64 target construction rejected"); 253 EXPECT(t == NULL, "wasm64 rejected target leaves target NULL"); 254 EXPECT(strstr(g_u.last_diag, "wasm64 is not supported") != NULL, 255 "wasm64 unsupported diagnostic"); 256 257 g_u.last_diag[0] = '\0'; 258 EXPECT(make_target(spec, KIT_SLICE_LIT("wasm64"), NULL, 0, &t) == 259 KIT_INVALID, 260 "wasm64 ISA spelling rejected"); 261 EXPECT(t == NULL, "wasm64 ISA rejected target leaves target NULL"); 262 EXPECT(strstr(g_u.last_diag, "unsupported ISA/profile") != NULL, 263 "wasm64 ISA diagnostic"); 264 } 265 266 static void check_target_profile_registry(void) { 267 static const char* const audited[] = { 268 "linux-glibc-aa64", "linux-glibc-x64", "linux-glibc-rv64", 269 "linux-musl-aa64", "linux-musl-x64", "linux-musl-rv64", 270 "freebsd-aa64", "freebsd-x64", "freebsd-rv64", 271 "windows-aa64", "windows-x64", "android-aa64", 272 "macos-aa64", "macos-x64", "freestanding-aa64", 273 "freestanding-x64", "freestanding-rv64", "freestanding-rv32", 274 "freestanding-arm32", 275 }; 276 const KitTargetProfile* profiles; 277 const KitTargetProfile* glibc; 278 const KitTargetProfile* musl; 279 size_t count = 0; 280 size_t i; 281 282 profiles = kit_target_profiles(&count); 283 EXPECT(profiles != NULL, "profile registry returned"); 284 EXPECT(count >= sizeof audited / sizeof audited[0], 285 "profile registry covers all audited cells"); 286 for (i = 0; i < sizeof audited / sizeof audited[0]; ++i) { 287 const KitTargetProfile* p = kit_target_profile_lookup(audited[i]); 288 EXPECT(p != NULL, "audited profile selector is registered"); 289 if (!p) continue; 290 EXPECT(p->selector != NULL && p->triple != NULL, 291 "profile has selector and triple"); 292 EXPECT((p->capabilities & KIT_TARGET_CAP_COMPILE) != 0, 293 "profile advertises compile capability"); 294 EXPECT(strcmp(kit_target_arch_name(p->spec.arch), "unknown") != 0 && 295 strcmp(kit_target_os_name(p->spec.os), "unknown") != 0 && 296 strcmp(kit_target_obj_name(p->spec.obj), "unknown") != 0, 297 "profile axes have stable names"); 298 EXPECT(kit_target_profile_lookup(p->triple) == p, 299 "profile triple lookup returns same record"); 300 } 301 302 glibc = kit_target_profile_lookup("aarch64-linux-gnu"); 303 musl = kit_target_profile_lookup("aarch64-linux-musl"); 304 EXPECT(glibc && musl && glibc->libc == KIT_TARGET_LIBC_GLIBC && 305 musl->libc == KIT_TARGET_LIBC_MUSL, 306 "registry distinguishes Linux libc profiles"); 307 EXPECT(kit_target_profile_lookup("wasm32-wasi") != NULL, 308 "compiled-in Wasm profile is registered"); 309 EXPECT((kit_target_profile_lookup("linux-glibc-aa64")->capabilities & 310 KIT_TARGET_CAP_SHARED) != 0 && 311 (kit_target_profile_lookup("android-aa64")->capabilities & 312 KIT_TARGET_CAP_SHARED) != 0, 313 "ELF hosted profiles advertise shipped shared linking"); 314 EXPECT((kit_target_profile_lookup("windows-aa64")->capabilities & 315 KIT_TARGET_CAP_SHARED) == 0 && 316 (kit_target_profile_lookup("windows-x64")->capabilities & 317 KIT_TARGET_CAP_SHARED) == 0 && 318 (kit_target_profile_lookup("macos-aa64")->capabilities & 319 KIT_TARGET_CAP_SHARED) == 0 && 320 (kit_target_profile_lookup("macos-x64")->capabilities & 321 KIT_TARGET_CAP_SHARED) == 0, 322 "Mach-O/COFF profiles do not advertise rejected shared output"); 323 EXPECT(kit_target_profile_lookup("definitely-not-a-target") == NULL, 324 "unknown profile is absent"); 325 EXPECT(KIT_TARGET_PROFILE_REGISTRY_VERSION == 1u, 326 "registry schema version is pinned"); 327 } 328 329 static void* hosted_test_alloc(void* user, size_t size) { 330 (void)user; 331 return malloc(size); 332 } 333 334 static void hosted_test_free(void* user, void* ptr, size_t size) { 335 (void)user; 336 (void)size; 337 free(ptr); 338 } 339 340 static int hosted_test_path_exists(void* user, const char* path) { 341 (void)user; 342 (void)path; 343 return 0; 344 } 345 346 static void hosted_test_clobber_stack(void) { 347 volatile unsigned char bytes[256]; 348 size_t i; 349 for (i = 0; i < sizeof bytes; ++i) bytes[i] = 0xa5u; 350 } 351 352 static void check_android_api_define_lifetime(void) { 353 static const KitOsHostedHost host = { 354 .path_exists = hosted_test_path_exists, 355 .alloc = hosted_test_alloc, 356 .free = hosted_test_free, 357 }; 358 const KitOsImpl* os = kit_os_lookup(KIT_OS_ANDROID); 359 KitOsHostedRequest req; 360 KitOsHostedDirs dirs; 361 KitOsHostedPlan plan; 362 uint32_t i; 363 int found = 0; 364 365 memset(&req, 0, sizeof req); 366 memset(&dirs, 0, sizeof dirs); 367 memset(&plan, 0, sizeof plan); 368 req.host = &host; 369 req.tool = "target_test"; 370 req.target = target_spec(KIT_ARCH_ARM_64, KIT_OS_ANDROID, KIT_OBJ_ELF); 371 req.target.os_version_major = 24; 372 dirs.host = &host; 373 dirs.libdirs[0] = (char*)"/ndk/lib"; 374 dirs.nlibdirs = 1; 375 376 EXPECT(os && os->hosted && os->hosted->resolve, 377 "Android hosted resolver is registered"); 378 if (!os || !os->hosted || !os->hosted->resolve) return; 379 EXPECT(os->hosted->resolve(&req, &dirs, &plan) == 0, 380 "Android hosted plan resolves"); 381 hosted_test_clobber_stack(); 382 for (i = 0; i < plan.ndefines; ++i) { 383 if (plan.defines[i].name.len == strlen("__ANDROID_API__") && 384 memcmp(plan.defines[i].name.s, "__ANDROID_API__", 385 strlen("__ANDROID_API__")) == 0) { 386 found = 1; 387 EXPECT(plan.defines[i].body.len == 2 && 388 memcmp(plan.defines[i].body.s, "24", 2) == 0, 389 "Android API define owns the exact resolved API integer"); 390 } 391 } 392 EXPECT(found, "Android API define is present"); 393 kit_os_hosted_plan_fini(&host, NULL, &plan); 394 } 395 396 static void check_errors(void) { 397 KitTargetSpec spec = target_spec(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); 398 KitTargetFeature bad_feature[] = { 399 {KIT_SLICE_LIT("nope"), true}, 400 }; 401 KitTarget* t = NULL; 402 403 g_u.last_diag[0] = '\0'; 404 EXPECT(make_target(spec, KIT_SLICE_NULL, bad_feature, 1, &t) == KIT_INVALID, 405 "unknown feature rejected"); 406 EXPECT(t == NULL, "unknown feature leaves target NULL"); 407 EXPECT(strstr(g_u.last_diag, "unknown target feature") != NULL, 408 "unknown feature diagnostic"); 409 410 g_u.last_diag[0] = '\0'; 411 EXPECT( 412 make_target(spec, KIT_SLICE_LIT("x86-64-v9"), NULL, 0, &t) == KIT_INVALID, 413 "unknown ISA/profile rejected"); 414 EXPECT(t == NULL, "unknown ISA/profile leaves target NULL"); 415 EXPECT(strstr(g_u.last_diag, "unsupported ISA/profile") != NULL, 416 "unknown ISA/profile diagnostic"); 417 } 418 419 int main(void) { 420 kit_unit_init(&g_u); 421 check_target_triple_parse(); 422 check_x64_defaults_and_isa(); 423 check_rv64_isa_and_overrides(); 424 check_wasm_features(); 425 check_target_profile_registry(); 426 check_android_api_define_lifetime(); 427 check_errors(); 428 kit_unit_summary(&g_u, "target_test"); 429 return kit_unit_status(&g_u); 430 }