kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }