kit

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

target.c (14645B)


      1 #include <stddef.h>
      2 #include <stdint.h>
      3 #include <stdio.h>
      4 #include <string.h>
      5 
      6 #include "driver.h"
      7 
      8 /* Pure target-triple parsing. No host I/O — just string walking — so this
      9  * lives outside driver/env.c (which is the syscall/host-state abstraction
     10  * layer). */
     11 
     12 static int triple_tok_eq(const char* s, size_t n, const char* lit) {
     13   size_t l = kit_slice_cstr(lit).len;
     14   return n == l && memcmp(s, lit, n) == 0;
     15 }
     16 
     17 /* Prefix match for OS tokens that carry a trailing version, e.g. clang emits
     18  * "freebsd15.0" / "freebsd14" rather than a bare "freebsd". */
     19 static int triple_tok_prefix(const char* s, size_t n, const char* lit) {
     20   size_t l = kit_slice_cstr(lit).len;
     21   return n >= l && memcmp(s, lit, l) == 0;
     22 }
     23 
     24 /* Recognize an architecture token, the single authority for the arch-name
     25  * spellings the driver accepts. Writes arch + natural pointer size on a hit.
     26  * Returns 0 on success, nonzero for an unrecognized token. Shared by the
     27  * triple parser and the public driver_arch_from_name. */
     28 static int arch_from_tok(const char* s, size_t n, KitArchKind* arch_out,
     29                          uint8_t* ptr_size_out) {
     30   KitArchKind arch;
     31   uint8_t ptr_size;
     32   if (triple_tok_eq(s, n, "x86_64") || triple_tok_eq(s, n, "amd64") ||
     33       triple_tok_eq(s, n, "x64")) {
     34     arch = KIT_ARCH_X86_64;
     35     ptr_size = 8;
     36   } else if (triple_tok_eq(s, n, "i386") || triple_tok_eq(s, n, "i486") ||
     37              triple_tok_eq(s, n, "i586") || triple_tok_eq(s, n, "i686")) {
     38     arch = KIT_ARCH_X86_32;
     39     ptr_size = 4;
     40   } else if (triple_tok_eq(s, n, "aarch64") || triple_tok_eq(s, n, "arm64") ||
     41              triple_tok_eq(s, n, "aa64")) {
     42     arch = KIT_ARCH_ARM_64;
     43     ptr_size = 8;
     44   } else if (triple_tok_eq(s, n, "arm") || triple_tok_eq(s, n, "armv7")) {
     45     arch = KIT_ARCH_ARM_32;
     46     ptr_size = 4;
     47   } else if (triple_tok_eq(s, n, "riscv64") || triple_tok_eq(s, n, "rv64")) {
     48     arch = KIT_ARCH_RV64;
     49     ptr_size = 8;
     50   } else if (triple_tok_eq(s, n, "riscv32") || triple_tok_eq(s, n, "rv32")) {
     51     arch = KIT_ARCH_RV32;
     52     ptr_size = 4;
     53   } else if (triple_tok_eq(s, n, "wasm32")) {
     54     arch = KIT_ARCH_WASM;
     55     ptr_size = 4;
     56   } else if (triple_tok_eq(s, n, "wasm64")) {
     57     arch = KIT_ARCH_WASM;
     58     ptr_size = 8;
     59   } else {
     60     return 1;
     61   }
     62   if (arch_out) *arch_out = arch;
     63   if (ptr_size_out) *ptr_size_out = ptr_size;
     64   return 0;
     65 }
     66 
     67 int driver_arch_from_name(const char* name, KitArchKind* arch_out,
     68                           uint8_t* ptr_size_out) {
     69   if (!name) return 1;
     70   return arch_from_tok(name, kit_slice_cstr(name).len, arch_out, ptr_size_out);
     71 }
     72 
     73 KitPic driver_default_pic(KitObjFmt obj, KitOSKind os) {
     74   /* WASM has no PIC/PIE concept; freestanding targets have no dynamic
     75    * loader to apply load-time relocations. Everything else is hosted and
     76    * defaults to PIE. */
     77   if (obj == KIT_OBJ_WASM) return KIT_PIC_NONE;
     78   if (os == KIT_OS_FREESTANDING) return KIT_PIC_NONE;
     79   return KIT_PIC_PIE;
     80 }
     81 
     82 int driver_link_pie(KitTargetSpec target, int explicit_pie, int shared,
     83                     int relocatable) {
     84   if (explicit_pie) return 1;
     85   if (shared || relocatable) return 0;
     86   return target.pic == KIT_PIC_PIE;
     87 }
     88 
     89 const char* driver_default_exe_name(KitTargetSpec target) {
     90   /* PE/COFF executables conventionally carry a `.exe` suffix; ELF/Mach-O
     91    * default link output is the historical `a.out`. */
     92   return target.os == KIT_OS_WINDOWS ? "a.exe" : "a.out";
     93 }
     94 
     95 void driver_default_obj_ext(KitTargetSpec target, const char** ext_out,
     96                             size_t* ext_len_out) {
     97   /* Windows targets default to a `.obj` suffix; everyone else `.o`. Drivers
     98    * accept both spellings as inputs, but tooling that scrapes default outputs
     99    * expects the canonical platform extension. */
    100   if (target.os == KIT_OS_WINDOWS) {
    101     if (ext_out) *ext_out = ".obj";
    102     if (ext_len_out) *ext_len_out = 4u;
    103   } else {
    104     if (ext_out) *ext_out = ".o";
    105     if (ext_len_out) *ext_len_out = 2u;
    106   }
    107 }
    108 
    109 int driver_target_needs_sysroot_libdir(KitTargetSpec target) {
    110   /* Windows targets fold `<sysroot>/lib` into the library search path (the
    111    * mingw import-library tree). The POSIX hosted profiles enumerate their
    112    * libdirs through the hosted resolver instead. */
    113   return target.os == KIT_OS_WINDOWS ? 1 : 0;
    114 }
    115 
    116 int driver_target_default_hosted_profile(KitTargetSpec target) {
    117   /* Windows-COFF is the one target whose hosted libc profile is engaged by
    118    * default (given a sysroot and no -nostdlib): the mingw/ucrt import
    119    * libraries are mandatory to produce a runnable PE. Other targets stay
    120    * freestanding unless the user opts in (-lc / explicit sysroot wiring). */
    121   return target.os == KIT_OS_WINDOWS && target.obj == KIT_OBJ_COFF ? 1 : 0;
    122 }
    123 
    124 static int target_features_grow(DriverTargetFeatures* tf) {
    125   DriverEnv* env = tf->env;
    126   uint32_t old_cap = tf->cap_features;
    127   uint32_t new_cap = old_cap ? old_cap * 2u : 8u;
    128   size_t old_sz = (size_t)old_cap * sizeof(*tf->features);
    129   size_t new_sz = (size_t)new_cap * sizeof(*tf->features);
    130   void* p = env->heap->realloc(env->heap, tf->features, old_sz, new_sz,
    131                                _Alignof(KitTargetFeature));
    132   if (!p) return 1;
    133   tf->features = (KitTargetFeature*)p;
    134   memset(tf->features + old_cap, 0, new_sz - old_sz);
    135   tf->cap_features = new_cap;
    136   return 0;
    137 }
    138 
    139 int driver_target_features_init(DriverTargetFeatures* tf, DriverEnv* env,
    140                                 int argc_bound) {
    141   size_t bound = argc_bound > 0 ? (size_t)argc_bound + 8u : 8u;
    142   if (!tf || !env || !env->heap) return 1;
    143   memset(tf, 0, sizeof(*tf));
    144   tf->env = env;
    145   tf->cap_features = (uint32_t)bound;
    146   tf->features = driver_alloc_zeroed(env, bound * sizeof(*tf->features));
    147   if (!tf->features) return 1;
    148   return 0;
    149 }
    150 
    151 void driver_target_features_fini(DriverTargetFeatures* tf, DriverEnv* env) {
    152   if (!tf || !env) return;
    153   driver_free(env, tf->features,
    154               (size_t)tf->cap_features * sizeof(*tf->features));
    155   memset(tf, 0, sizeof(*tf));
    156 }
    157 
    158 static int target_features_record_feature(DriverTargetFeatures* tf,
    159                                           DriverEnv* env, KitSlice name,
    160                                           int enabled) {
    161   KitTargetFeature* f;
    162   (void)env;
    163   if (!name.s || name.len == 0) return 1;
    164   if (tf->nfeatures >= tf->cap_features && target_features_grow(tf) != 0)
    165     return 1;
    166   f = &tf->features[tf->nfeatures++];
    167   f->name = name;
    168   f->enabled = enabled ? true : false;
    169   return 0;
    170 }
    171 
    172 static const char* target_features_pull_value(const char* tool, int argc,
    173                                               char** argv, int* i,
    174                                               size_t prefix_len,
    175                                               const char* flag_label) {
    176   const char* a = argv[*i];
    177   if (a[prefix_len]) return a + prefix_len;
    178   if (++(*i) >= argc) {
    179     driver_errf(tool, "%.*s requires an argument",
    180                 KIT_SLICE_ARG(kit_slice_cstr(flag_label)));
    181     return NULL;
    182   }
    183   return argv[*i];
    184 }
    185 
    186 static int target_features_parse_mattr(DriverTargetFeatures* tf, DriverEnv* env,
    187                                        const char* tool, const char* list) {
    188   const char* p = list;
    189   if (!p || !*p) {
    190     driver_errf(tool, "-mattr= requires a comma-separated feature list");
    191     return 1;
    192   }
    193   while (*p) {
    194     int enabled;
    195     const char* start;
    196     if (*p == '+') {
    197       enabled = 1;
    198     } else if (*p == '-') {
    199       enabled = 0;
    200     } else {
    201       driver_errf(tool, "-mattr entries must start with '+' or '-': %.*s",
    202                   KIT_SLICE_ARG(kit_slice_cstr(p)));
    203       return 1;
    204     }
    205     ++p;
    206     start = p;
    207     while (*p && *p != ',') ++p;
    208     if (p == start) {
    209       driver_errf(tool, "empty -mattr feature");
    210       return 1;
    211     }
    212     if (target_features_record_feature(
    213             tf, env, (KitSlice){.s = start, .len = (size_t)(p - start)},
    214             enabled) != 0) {
    215       driver_errf(tool, "out of memory");
    216       return 1;
    217     }
    218     if (*p == ',') ++p;
    219   }
    220   return 0;
    221 }
    222 
    223 int driver_target_features_try_consume(DriverTargetFeatures* tf, DriverEnv* env,
    224                                        const char* tool, int argc, char** argv,
    225                                        int* i) {
    226   const char* a = argv[*i];
    227   const char* v;
    228   if (!tf || !a) return 0;
    229 
    230   if (driver_strneq(a, "-mattr=", 7)) {
    231     return target_features_parse_mattr(tf, env, tool, a + 7) == 0 ? 1 : -1;
    232   }
    233   if (driver_streq(a, "-mattr")) {
    234     if (++(*i) >= argc) {
    235       driver_errf(tool, "-mattr requires an argument");
    236       return -1;
    237     }
    238     return target_features_parse_mattr(tf, env, tool, argv[*i]) == 0 ? 1 : -1;
    239   }
    240   if (driver_strneq(a, "-mfeature=", 10) ||
    241       driver_strneq(a, "-mno-feature=", 13)) {
    242     return 0;
    243   }
    244   if (driver_strneq(a, "-march=", 7)) {
    245     tf->isa = kit_slice_cstr(a + 7);
    246     return 1;
    247   }
    248   if (driver_streq(a, "-march")) {
    249     if (++(*i) >= argc) {
    250       driver_errf(tool, "-march requires an argument");
    251       return -1;
    252     }
    253     tf->isa = kit_slice_cstr(argv[*i]);
    254     return 1;
    255   }
    256   if (driver_strneq(a, "-mabi=", 6)) {
    257     tf->abi = kit_slice_cstr(a + 6);
    258     return 1;
    259   }
    260   if (driver_streq(a, "-mabi")) {
    261     if (++(*i) >= argc) {
    262       driver_errf(tool, "-mabi requires an argument");
    263       return -1;
    264     }
    265     tf->abi = kit_slice_cstr(argv[*i]);
    266     return 1;
    267   }
    268   if (driver_strneq(a, "-mcpu=", 6)) {
    269     tf->cpu = kit_slice_cstr(a + 6);
    270     return 1;
    271   }
    272   if (driver_streq(a, "-mcpu")) {
    273     v = target_features_pull_value(tool, argc, argv, i, 5, "-mcpu");
    274     if (!v) return -1;
    275     tf->cpu = kit_slice_cstr(v);
    276     return 1;
    277   }
    278   if (driver_strneq(a, "-mtune=", 7)) {
    279     tf->tune = kit_slice_cstr(a + 7);
    280     return 1;
    281   }
    282   if (driver_streq(a, "-mtune")) {
    283     v = target_features_pull_value(tool, argc, argv, i, 6, "-mtune");
    284     if (!v) return -1;
    285     tf->tune = kit_slice_cstr(v);
    286     return 1;
    287   }
    288   if (driver_strneq(a, "-mno-", 5) && a[5] != '\0') {
    289     if (target_features_record_feature(tf, env, kit_slice_cstr(a + 5), 0) !=
    290         0) {
    291       driver_errf(tool, "out of memory");
    292       return -1;
    293     }
    294     return 1;
    295   }
    296   if (driver_strneq(a, "-m", 2) && a[2] != '\0') {
    297     if (target_features_record_feature(tf, env, kit_slice_cstr(a + 2), 1) !=
    298         0) {
    299       driver_errf(tool, "out of memory");
    300       return -1;
    301     }
    302     return 1;
    303   }
    304   return 0;
    305 }
    306 
    307 int driver_target_options(const DriverTargetFeatures* tf, const char* tool,
    308                           KitTargetSpec target, KitTargetOptions* out) {
    309   (void)tool;
    310   if (!out) return 1;
    311   memset(out, 0, sizeof(*out));
    312   out->spec = target;
    313   if (!tf) return 0;
    314   out->isa = tf->isa;
    315   out->cpu = tf->cpu;
    316   out->tune = tf->tune;
    317   out->abi = tf->abi;
    318   out->features = tf->features;
    319   out->nfeatures = tf->nfeatures;
    320   return 0;
    321 }
    322 
    323 KitStatus driver_target_new(const KitContext* ctx, KitTargetSpec target,
    324                             const DriverTargetFeatures* tf, const char* tool,
    325                             KitTarget** out) {
    326   KitTargetOptions opts;
    327   if (driver_target_options(tf, tool, target, &opts) != 0) {
    328     if (out) *out = NULL;
    329     return KIT_INVALID;
    330   }
    331   return kit_target_new(ctx, &opts, out);
    332 }
    333 
    334 int driver_target_from_triple(const char* triple, KitTargetSpec* out) {
    335   const char* parts[4];
    336   size_t plen[4];
    337   int np = 0;
    338   const char* p;
    339   KitTargetSpec t;
    340   int os_set;
    341   int i;
    342 
    343   if (!triple || !out) return 1;
    344   memset(&t, 0, sizeof(t));
    345 
    346   p = triple;
    347   while (np < 4) {
    348     const char* dash = driver_strchr(p, '-');
    349     parts[np] = p;
    350     plen[np] = dash ? (size_t)(dash - p) : kit_slice_cstr(p).len;
    351     if (plen[np] == 0) return 1;
    352     np++;
    353     if (!dash) break;
    354     p = dash + 1;
    355   }
    356 
    357   if (arch_from_tok(parts[0], plen[0], &t.arch, &t.ptr_size) != 0) return 1;
    358 
    359   os_set = 0;
    360   for (i = 1; i < np; ++i) {
    361     if (triple_tok_eq(parts[i], plen[i], "linux")) {
    362       t.os = KIT_OS_LINUX;
    363       t.obj = KIT_OBJ_ELF;
    364       os_set = 1;
    365       break;
    366     }
    367     if (triple_tok_eq(parts[i], plen[i], "darwin") ||
    368         triple_tok_eq(parts[i], plen[i], "macos")) {
    369       t.os = KIT_OS_MACOS;
    370       t.obj = KIT_OBJ_MACHO;
    371       os_set = 1;
    372       break;
    373     }
    374     if (triple_tok_eq(parts[i], plen[i], "windows") ||
    375         triple_tok_eq(parts[i], plen[i], "win32")) {
    376       t.os = KIT_OS_WINDOWS;
    377       t.obj = KIT_OBJ_COFF;
    378       os_set = 1;
    379       break;
    380     }
    381     if (triple_tok_prefix(parts[i], plen[i], "freebsd")) {
    382       const char* ver = parts[i] + 7; /* skip "freebsd" */
    383       size_t rem = plen[i] - 7;
    384       unsigned v = 0;
    385       size_t j;
    386       for (j = 0; j < rem && ver[j] >= '0' && ver[j] <= '9'; ++j)
    387         v = v * 10 + (unsigned)(ver[j] - '0');
    388       t.os = KIT_OS_FREEBSD;
    389       t.obj = KIT_OBJ_ELF;
    390       t.os_version_major = (uint8_t)(v > 255 ? 0 : v);
    391       os_set = 1;
    392       break;
    393     }
    394     if (triple_tok_eq(parts[i], plen[i], "wasi")) {
    395       t.os = KIT_OS_WASI;
    396       t.obj = KIT_OBJ_WASM;
    397       os_set = 1;
    398       break;
    399     }
    400     if (triple_tok_eq(parts[i], plen[i], "none") ||
    401         triple_tok_eq(parts[i], plen[i], "freestanding")) {
    402       t.os = KIT_OS_FREESTANDING;
    403       t.obj = (t.arch == KIT_ARCH_WASM) ? KIT_OBJ_WASM : KIT_OBJ_ELF;
    404       os_set = 1;
    405       break;
    406     }
    407   }
    408   if (!os_set) {
    409     t.os = KIT_OS_FREESTANDING;
    410     t.obj = (t.arch == KIT_ARCH_WASM) ? KIT_OBJ_WASM : KIT_OBJ_ELF;
    411   }
    412 
    413   t.ptr_align = t.ptr_size;
    414   t.big_endian = 0;
    415   t.pic = driver_default_pic(t.obj, t.os);
    416   t.code_model = KIT_CM_DEFAULT;
    417 
    418   *out = t;
    419   return 0;
    420 }
    421 
    422 int driver_target_to_triple(KitTargetSpec target, char* buf, size_t cap) {
    423   const char* arch;
    424   const char* os;
    425   int n;
    426   if (!buf || cap == 0) return 1;
    427 
    428   switch (target.arch) {
    429     case KIT_ARCH_X86_64:
    430       arch = "x86_64";
    431       break;
    432     case KIT_ARCH_X86_32:
    433       arch = "i386";
    434       break;
    435     case KIT_ARCH_ARM_64:
    436       arch = "aarch64";
    437       break;
    438     case KIT_ARCH_ARM_32:
    439       arch = "arm";
    440       break;
    441     case KIT_ARCH_RV64:
    442       arch = "riscv64";
    443       break;
    444     case KIT_ARCH_RV32:
    445       arch = "riscv32";
    446       break;
    447     case KIT_ARCH_WASM:
    448       arch = target.ptr_size == 8 ? "wasm64" : "wasm32";
    449       break;
    450     default:
    451       arch = "unknown";
    452       break;
    453   }
    454 
    455   switch (target.os) {
    456     case KIT_OS_LINUX:
    457       os = "linux";
    458       break;
    459     case KIT_OS_MACOS:
    460       os = "apple-darwin";
    461       break;
    462     case KIT_OS_WINDOWS:
    463       os = "windows";
    464       break;
    465     case KIT_OS_FREEBSD:
    466       os = "freebsd";
    467       break;
    468     case KIT_OS_WASI:
    469       os = "wasi";
    470       break;
    471     case KIT_OS_FREESTANDING:
    472     default:
    473       os = "elf";
    474       break;
    475   }
    476 
    477   n = snprintf(buf, cap, "%.*s-%.*s", KIT_SLICE_ARG(kit_slice_cstr(arch)),
    478                KIT_SLICE_ARG(kit_slice_cstr(os)));
    479   return n < 0 || (size_t)n >= cap;
    480 }