skip to content
==> src/main.c <==
1/* mesh-ptx-link: links LLVM bitcode into one PTX module in the cluster float
2 * mode (f32 FTZ, unfused), keeping only the exported symbols.
3 *
4 *   mesh-ptx-link --mcpu sm_86 [-O0..3] [--debug] [--export SYM]... -o OUT.ptx IN.bc...
5 *
6 * As alpha-rust-mesh scripts/ptx-link.sh (rustc's linker for programs): the
7 * commands of rustc's llvm-bitcode-linker (llvm-link, opt, llc) with the float
8 * mode added. Tools: $MESH_LLVM_LINK, $MESH_OPT, $MESH_LLC, or found on PATH
9 * (also name-21 .. name-15) or under /usr/lib/llvm-N/bin. Any argument it
10 * does not know is refused. */
11#include "link.h"
12
13#include <errno.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/wait.h>
18#include <unistd.h>
19
20static int run(char **argv) {
21    pid_t pid = fork();
22    if (pid < 0) { fprintf(stderr, "mesh-ptx-link: fork: %s\n", strerror(errno)); return 1; }
23    if (pid == 0) {
24        execv(argv[0], argv);
25        fprintf(stderr, "mesh-ptx-link: %s: %s\n", argv[0], strerror(errno));
26        _exit(127);
27    }
28    int st;
29    while (waitpid(pid, &st, 0) < 0)
30        if (errno != EINTR) return 1;
31    if (WIFEXITED(st) && WEXITSTATUS(st) == 0) return 0;
32    fprintf(stderr, "mesh-ptx-link: %s failed\n", argv[0]);
33    return 1;
34}
35
36static int usage(void) {
37    fprintf(stderr, "usage: mesh-ptx-link --mcpu sm_NN [-O0..3] [--debug] [--export SYM]... -o OUT.ptx IN.bc...\n");
38    return 2;
39}
40
41int main(int argc, char **argv) {
42    const char **inputs = calloc((size_t)argc, sizeof *inputs), **exports = calloc((size_t)argc, sizeof *exports);
43    if (!inputs || !exports) return 1;
44    mesh_ptx_link l = {.opt = 3};
45    size_t ni = 0, ne = 0;
46    for (int i = 1; i < argc; i++) {
47        const char *a = argv[i];
48        if ((!strcmp(a, "--mcpu") || !strcmp(a, "--target-cpu")) && i + 1 < argc) l.cpu = argv[++i];
49        else if ((!strcmp(a, "-o") || !strcmp(a, "--output")) && i + 1 < argc) l.output = argv[++i];
50        else if (!strcmp(a, "--export") && i + 1 < argc) exports[ne++] = argv[++i];
51        else if (!strcmp(a, "--debug")) l.debug = true;
52        else if (a[0] == '-' && a[1] == 'O' && a[2] >= '0' && a[2] <= '3' && !a[3]) l.opt = a[2] - '0';
53        else if (a[0] == '-') { fprintf(stderr, "mesh-ptx-link: unknown option %s\n", a); return usage(); }
54        else inputs[ni++] = a;
55    }
56    if (!l.output || !l.cpu || ni == 0) { fprintf(stderr, "mesh-ptx-link: need -o, --mcpu and inputs\n"); return usage(); }
57    if (!mesh_ptx_link_valid_arch(l.cpu)) { fprintf(stderr, "mesh-ptx-link: --mcpu %s: want sm_NN (e.g. sm_86, sm_121)\n", l.cpu); return 1; }
58    l.inputs = inputs;
59    l.ninputs = ni;
60    if (ne) { l.exports = exports; l.nexports = ne; }
61    char *llvm_link = mesh_ptx_link_find_tool("llvm-link", "MESH_LLVM_LINK");
62    char *opt = mesh_ptx_link_find_tool("opt", "MESH_OPT");
63    char *llc = mesh_ptx_link_find_tool("llc", "MESH_LLC");
64    const char *missing = !llvm_link ? "llvm-link ($MESH_LLVM_LINK)" : !opt ? "opt ($MESH_OPT)" : !llc ? "llc ($MESH_LLC)" : NULL;
65    if (missing) { fprintf(stderr, "mesh-ptx-link: no %s: install LLVM's tools\n", missing); return 1; }
66    l.llvm_link = llvm_link;
67    l.opt_tool = opt;
68    l.llc = llc;
69    char *syms = mesh_ptx_link_symbols(&l), *symfile = mesh_ptx_link_side(&l, ".symbols.txt");
70    FILE *f = fopen(symfile, "w");
71    if (!f || fputs(syms, f) < 0 || fclose(f) != 0) { fprintf(stderr, "mesh-ptx-link: %s: %s\n", symfile, strerror(errno)); return 1; }
72    char **steps[3] = {mesh_ptx_link_argv_link(&l), mesh_ptx_link_argv_opt(&l), mesh_ptx_link_argv_llc(&l)};
73    int rc = 0;
74    for (int i = 0; i < 3 && rc == 0; i++) rc = run(steps[i]);
75    for (int i = 0; i < 3; i++) mesh_ptx_link_free_argv(steps[i]);
76    free(syms), free(symfile), free(llvm_link), free(opt), free(llc), free(inputs), free(exports);
77    return rc;
78}