1DOLLY 72PACKAGE mojo3REQUIRES HOST runtime@0456789FROM /v0.1.4/demos/mojo/Dollyfile-mojo-llvm 87f031c31c024255d664a5a584c23a5bcc759aced3e5892b300f7a28e390109d1011INSTALL /v0.1.4/Dollyfile-libdolly-gpu 1308d574fd8b030fac77cf9814dc9925d2737be130a6401b9929c80e13307bf212INSTALL /v0.1.4/Dollyfile-cc dadb1af6ae3450a3a48bf4c955fe40d012a9a20d6dc3eca63c4f2737ab4080cc13REQUIRES HEADER gpu14REQUIRES LIB dolly-gpu15REQUIRES TOOL cmake16REQUIRES TOOL make17REQUIRES TOOL python18REQUIRES TOOL cc19REQUIRES TOOL c++20REQUIRES TOOL ar21REQUIRES TOOL gzip22REQUIRES TOOL tar23REQUIRES TOOL patch24REQUIRES TOOL time25REQUIRES TOOL cp26REQUIRES TOOL mkdir27REQUIRES TOOL rm28REQUIRES TOOL sha256sum29REQUIRES TOOL cut3031SOURCE /v0.1.4/dist/static/mojo/mojo.tar.gz 85c382a809ee195c5d153de052aef38618ef1466d9f54a603a2e486445fda9fd /tmp/mojo.tar.gz32SLOP slop -c 'gzip -dc /tmp/mojo.tar.gz | tar -xf - -C /'3334SLOP patch -p1 -d /tmp/mojo/modular -i /tmp/mojo/mojo-dolly.patch353637SLOP patch -p1 -d /tmp/mojo/modular -i /tmp/mojo/target/patches/compiler.patch38SLOP patch -p1 -d /tmp/mojo/modular -i /tmp/mojo/target/patches/stdlib.patch3940SLOP slop -c 'python /tmp/mojo/bazel-to-cmake.py /tmp/mojo/modular > /tmp/mojo/CMakeLists.txt'4142434445FILE /tmp/mojo/Dolly.cmake46 set(CMAKE_BUILD_TYPE Release CACHE STRING "")47 set(CMAKE_C_FLAGS "-std=gnu17" CACHE STRING "")48 set(CMAKE_CXX_FLAGS "-include endian.h -DCLANG_BUILD_STATIC" CACHE STRING "")49 set(CMAKE_EXE_LINKER_FLAGS "-Wl,--initial-memory=67108864" CACHE STRING "")50 set(CMAKE_RULE_MESSAGES OFF CACHE BOOL "")51 set(MOJO_SOURCE /tmp/mojo/modular CACHE PATH "")52 set(MOJO_PORT /tmp/mojo/port CACHE PATH "")53 set(MOJO_LLVM /opt/mojo-llvm CACHE PATH "")54 set(FMT /tmp/mojo/fmt CACHE PATH "")55 set(XXHASH /tmp/mojo/xxhash CACHE PATH "")56 set(OPENTELEMETRY /tmp/mojo/opentelemetry CACHE PATH "")57 set(MOJO_VERSION_LABEL "" CACHE STRING "")585960SLOP time slop -c 'cmake -C /tmp/mojo/Dolly.cmake -DMODULAR_VERSION_REVISION=c6fa49f7+dolly.$(sha256sum /tmp/mojo.tar.gz | cut -c1-8) -S /tmp/mojo -B /tmp/mojo/build'61626364SLOP CWD /tmp/mojo/build time make -k -j3 Mojo.tools.mojo.mojo65SLOP mkdir -p /usr/lib/mojo /usr/share/licenses/mojo66SLOP cp /tmp/mojo/build/mojo /usr/bin/mojo67SLOP /usr/bin/mojo --version6869SLOP time /usr/bin/mojo precompile -o /usr/lib/mojo/std.mojoc /tmp/mojo/modular/Mojo/stdlib/std70SLOP time /usr/bin/mojo precompile -I /usr/lib/mojo -o /usr/lib/mojo/gpu.mojoc /tmp/mojo/target/gpu7172SLOP cp /tmp/mojo/modular/LICENSE /usr/share/licenses/mojo/LICENSE73SLOP cp /opt/mojo-llvm/LICENSE.TXT /usr/share/licenses/mojo/LICENSE.llvm74SLOP cp /opt/mojo-llvm/LICENSE.zstd /usr/share/licenses/mojo/LICENSE.zstd75SLOP cp /tmp/mojo/fmt/LICENSE /usr/share/licenses/mojo/LICENSE.fmt76SLOP cp /tmp/mojo/xxhash/LICENSE /usr/share/licenses/mojo/LICENSE.xxhash77SLOP cp /tmp/mojo/opentelemetry/LICENSE /usr/share/licenses/mojo/LICENSE.opentelemetry78798081FILE /tmp/mojo-runtime/runtime.c82 /* The compiler runtime of a Mojo program in Dolly: the KGEN_CompilerRT_*83 * functions that the standard library and the compiler's output call.84 *85 * Upstream's is Mojo/lib/CompilerRT, over AsyncRT's thread pool, tcmalloc,86 * Support and LLVMSupport. This one is its interface over libc for one87 * thread: a task runs when something waits for it, and the parallelism level88 * is 1. A string argument is a pointer and a length, as the compiler emits a89 * two-word struct for wasm64. Python interoperation is absent, so a program90 * that uses it does not link. */9192939495969798 99 /* Memory */100 101 void *KGEN_CompilerRT_AlignedAlloc(int64_t alignment, int64_t size) {102 void *memory = NULL;103 if (alignment <= 0) alignment = 16;104 if (alignment < (int64_t)sizeof(void *)) alignment = sizeof(void *);105 return posix_memalign(&memory, (size_t)alignment, size > 0 ? (size_t)size : 1) ? NULL : memory;106 }107 108 void KGEN_CompilerRT_AlignedFree(void *memory) { free(memory); }109 110 /* Globals, destroyed latest first */111 112 struct global {113 struct global *next;114 size_t length;115 void *value;116 void (*destroy)(void *);117 char name[];118 };119 120 enum { INDEXED_GLOBALS = 3 };121 static struct global *globals;122 static struct { void *value; void (*destroy)(void *); } indexed[INDEXED_GLOBALS];123 124 void *KGEN_CompilerRT_GetOrCreateGlobal(const char *name, size_t length, void *(*create)(void),125 void (*destroy)(void *)) {126 for (struct global *entry = globals; entry; entry = entry->next) {127 if (entry->length == length && !memcmp(entry->name, name, length)) return entry->value;128 }129 if (!create) return NULL;130 void *value = create();131 struct global *entry = malloc(sizeof *entry + length);132 if (!entry) abort();133 *entry = (struct global){globals, length, value, destroy};134 memcpy(entry->name, name, length);135 globals = entry;136 return value;137 }138 139 void *KGEN_CompilerRT_GetGlobalOrNull(const char *name, size_t length) {140 return KGEN_CompilerRT_GetOrCreateGlobal(name, length, NULL, NULL);141 }142 143 static void *inserted;144 static void *take_inserted(void) { return inserted; }145 146 void KGEN_CompilerRT_InsertGlobal(const char *name, size_t length, void *value) {147 inserted = value;148 KGEN_CompilerRT_GetOrCreateGlobal(name, length, take_inserted, NULL);149 }150 151 void *KGEN_CompilerRT_GetOrCreateGlobalIndexed(size_t index, void *(*create)(void), void (*destroy)(void *)) {152 if (index >= INDEXED_GLOBALS) abort();153 if (!indexed[index].value) {154 indexed[index].value = create();155 indexed[index].destroy = destroy;156 }157 return indexed[index].value;158 }159 160 void KGEN_CompilerRT_DestroyGlobals(void) {161 while (globals) {162 struct global *entry = globals;163 globals = entry->next;164 if (entry->destroy && entry->value) entry->destroy(entry->value);165 free(entry);166 }167 for (size_t index = 0; index < INDEXED_GLOBALS; index++) {168 void *value = indexed[index].value;169 indexed[index].value = NULL;170 if (value && indexed[index].destroy) indexed[index].destroy(value);171 }172 }173 174 /* Clang names a C program's main(argc, argv) __main_argc_argv for Wasm, and175 * libc calls that. Mojo names its entry point main. */176 int mojo_main(int, char **) __asm__("main");177 int __main_argc_argv(int count, char **values) { return mojo_main(count, values); }178 179 /* Arguments */180 181 struct string { const char *bytes; size_t length; };182 struct argument_list { struct string *list; size_t count; };183 static struct string no_arguments = {"", 0}, *arguments = &no_arguments;184 static size_t argument_count = 1;185 186 void KGEN_CompilerRT_SetArgV(int count, char **values) {187 struct string *list = calloc(count > 0 ? (size_t)count : 1, sizeof *list);188 if (!list) abort();189 for (int i = 0; i < count; i++) list[i] = (struct string){values[i], strlen(values[i])};190 arguments = list;191 argument_count = (size_t)count;192 }193 194 void KGEN_CompilerRT_GetArgV(struct argument_list *result) {195 result->list = arguments;196 result->count = argument_count;197 }198 199 /* The environment for a spawned process: Mojo cannot name a C variable,200 * and Dolly's dlsym, which upstream asks on Linux, belongs to programs that201 * load modules. Dolly's patch of the standard library calls this. */202 extern char **environ;203 char **KGEN_CompilerRT_Environ(void) { return environ; }204 205 /* The system */206 207 size_t KGEN_CompilerRT_NumPhysicalCores(void) { return 1; }208 size_t KGEN_CompilerRT_NumLogicalCores(void) { return 1; }209 size_t KGEN_CompilerRT_NumPerformanceCores(void) { return 1; }210 211 int KGEN_CompilerRT_fprintf(FILE *stream, const char *format, ...) {212 va_list values;213 va_start(values, format);214 int result = vfprintf(stream, format, values);215 va_end(values);216 return result;217 }218 219 /* A fault in Wasm is a trap: the process ends with status 126 and one line220 * from Dolly's supervisor. There is no signal to print a stack trace from. */221 void KGEN_CompilerRT_PrintStackTraceOnFault(void) {}222 223 /* As upstream answers while stack traces on errors are not configured. */224 int64_t KGEN_CompilerRT_GetStackTrace(char **text, int64_t depth) {225 (void)text;226 (void)depth;227 return 0;228 }229 230 void KGEN_CompilerRT_SetAsanAllocators(void) {}231 232 /* Tasks: one queue, run by whoever waits */233 234 struct task {235 struct task *next;236 void (*resume)(void *);237 void *handle;238 };239 240 struct chain {241 bool ready;242 struct task *waiting;243 };244 245 static struct task *first_task, **last_task = &first_task;246 static char device;247 static void *current_device;248 249 static void enqueue(struct task *task) {250 task->next = NULL;251 *last_task = task;252 last_task = &task->next;253 }254 255 static bool run_one(void) {256 struct task *task = first_task;257 if (!task) return false;258 first_task = task->next;259 if (!first_task) last_task = &first_task;260 void (*resume)(void *) = task->resume;261 void *handle = task->handle;262 free(task);263 resume(handle);264 return true;265 }266 267 static struct task *new_task(void (*resume)(void *), void *handle) {268 struct task *task = malloc(sizeof *task);269 if (!task) abort();270 *task = (struct task){NULL, resume, handle};271 return task;272 }273 274 void KGEN_CompilerRT_AsyncRT_InitializeChain(struct chain **chain) {275 *chain = calloc(1, sizeof **chain);276 if (!*chain) abort();277 }278 279 void KGEN_CompilerRT_AsyncRT_DestroyChain(struct chain **chain) {280 free(*chain);281 *chain = NULL;282 }283 284 void KGEN_CompilerRT_AsyncRT_Complete(struct chain **chain) {285 (*chain)->ready = true;286 while ((*chain)->waiting) {287 struct task *task = (*chain)->waiting;288 (*chain)->waiting = task->next;289 enqueue(task);290 }291 }292 293 void KGEN_CompilerRT_AsyncRT_Wait(struct chain **chain) {294 while (!(*chain)->ready) {295 if (!run_one()) {296 fputs("mojo: waiting for a task that nothing will complete\n", stderr);297 abort();298 }299 }300 }301 302 bool KGEN_CompilerRT_AsyncRT_Wait_Timeout(struct chain **chain, int64_t nanoseconds) {303 struct timespec start, now;304 clock_gettime(CLOCK_MONOTONIC, &start);305 while (!(*chain)->ready && run_one()) {306 clock_gettime(CLOCK_MONOTONIC, &now);307 if ((now.tv_sec - start.tv_sec) * 1000000000 + (now.tv_nsec - start.tv_nsec) >= nanoseconds) break;308 }309 return (*chain)->ready;310 }311 312 void KGEN_CompilerRT_AsyncRT_Execute(void (*resume)(void *), void *handle, int64_t worker) {313 (void)worker;314 enqueue(new_task(resume, handle));315 }316 317 void KGEN_CompilerRT_AsyncRT_AndThen(void (*resume)(void *), struct chain **chain, void *handle) {318 struct task *task = new_task(resume, handle);319 if ((*chain)->ready) {320 enqueue(task);321 } else {322 task->next = (*chain)->waiting;323 (*chain)->waiting = task;324 }325 }326 327 void *KGEN_CompilerRT_AsyncRT_GetOrCreateCPUDevice(void) { return current_device = &device; }328 void *KGEN_CompilerRT_AsyncRT_GetCurrentCPUDevice(void) { return current_device; }329 void KGEN_CompilerRT_AsyncRT_ReleaseCPUDevice(void *released) { (void)released; }330 uint32_t KGEN_CompilerRT_AsyncRT_ParallelismLevel(void) { return 1; }331 332 /* A spin waiter waits for another task: here, by running one. */333 void *KGEN_CompilerRT_AsyncRT_InitializeSpinWaiter(void) { return &device; }334 void KGEN_CompilerRT_AsyncRT_SpinWaiter_Wait(void *waiter) {335 (void)waiter;336 run_one();337 }338 void KGEN_CompilerRT_AsyncRT_DestroySpinWaiter(void *waiter) { (void)waiter; }339340FILE /tmp/mojo-runtime/gpu.c341 /* The device side of the Mojo package `gpu` (gpu.host.DeviceContext) over342 * Dolly's gpu@0. A kernel is WGSL as the compiler's webgpu target prints it:343 * pointer parameters are storage buffers bound in order, the other parameters344 * are 32-bit slots of one more buffer, block_dim is the workgroup size, and345 * what it prints are records of a last buffer, `log`. */346347348349350351352353 354 enum { STORAGE = 128 | 4 | 8, READBACK = 1 | 8, CHUNK = 512 * 1024 };355 356 static dolly_gpu g;357 static uint64_t next = 1;358 359 static int64_t done(void) { return dolly_gpu_batch(&g) < 0 ? -errno : 0; }360 361 int64_t mojo_gpu_open(void) {362 if (g.scope) return 0;363 return dolly_gpu_open(&g, 0, 0) < 0 ? -errno : 0;364 }365 366 int64_t mojo_gpu_buffer(uint64_t bytes) {367 uint64_t id = next++;368 dolly_gpu_begin(&g);369 dolly_gpu_buffer(&g, id, (bytes + 3) & ~(uint64_t)3, STORAGE);370 int64_t error = done();371 return error ? error : (int64_t)id;372 }373 374 int64_t mojo_gpu_release(uint64_t id) {375 dolly_gpu_begin(&g);376 void *record = dolly_gpu_record(&g, DOLLY_GPU_RELEASE, 16);377 memcpy((char *)record + 8, &id, 8);378 return done();379 }380 381 int64_t mojo_gpu_write(uint64_t buffer, uint64_t offset, const void *data, uint64_t bytes) {382 for (uint64_t at = 0; at < bytes; at += CHUNK) {383 uint32_t size = bytes - at < CHUNK ? (uint32_t)(bytes - at) : CHUNK;384 /* WebGPU writes whole 32-bit words: the tail of a buffer of bytes is385 * padded with the zeros of the record, into the buffer's own padding. */386 uint32_t padded = (size + 3) & ~3u;387 dolly_gpu_begin(&g);388 char *record = dolly_gpu_record(&g, DOLLY_GPU_WRITE_BUFFER, 32 + padded);389 uint64_t target = offset + at;390 uint32_t data_offset = 32;391 memcpy(record + 8, &buffer, 8);392 memcpy(record + 16, &target, 8);393 memcpy(record + 24, &data_offset, 4);394 memcpy(record + 28, &padded, 4);395 memcpy(record + 32, (const char *)data + at, size);396 int64_t error = done();397 if (error) return error;398 }399 return 0;400 }401 402 /* gpu@0 maps at most 16 MiB at once and answers 64 KiB a call. */403 static int64_t read_mapped(uint64_t buffer, uint64_t offset, char *data, uint64_t bytes) {404 uint64_t staging = next++, size = (bytes + 3) & ~(uint64_t)3;405 dolly_gpu_begin(&g);406 dolly_gpu_buffer(&g, staging, size, READBACK);407 char *copy = dolly_gpu_record(&g, DOLLY_GPU_COPY_BUFFER, 48);408 memcpy(copy + 8, &buffer, 8);409 memcpy(copy + 16, &staging, 8);410 memcpy(copy + 24, &offset, 8);411 memcpy(copy + 40, &size, 8);412 dolly_gpu_submit(&g);413 dolly_gpu_map(&g, staging, size);414 int64_t error = done();415 for (uint64_t at = 0; !error && at < bytes; at += DOLLY_GPU_REPLY_BYTES) {416 uint64_t part = bytes - at < DOLLY_GPU_REPLY_BYTES ? bytes - at : DOLLY_GPU_REPLY_BYTES;417 if (dolly_gpu_read(&g, staging, at, part) != (int)part) error = errno ? -errno : -EIO;418 else memcpy(data + at, g.reply, part);419 }420 int64_t released = mojo_gpu_release(staging);421 return error ? error : released;422 }423 424 int64_t mojo_gpu_read(uint64_t buffer, uint64_t offset, void *data, uint64_t bytes) {425 enum { MAPPED = 16 * 1024 * 1024 };426 for (uint64_t at = 0; at < bytes; at += MAPPED) {427 int64_t error = read_mapped(buffer, offset + at, (char *)data + at, bytes - at < MAPPED ? bytes - at : MAPPED);428 if (error) return error;429 }430 return 0;431 }432 433 /* One shader module per kernel text and one pipeline per block_dim of it. */434 static struct { const char *code; uint64_t shader, pipeline; uint32_t block[3]; } kernels[64];435 436 static int64_t pipeline(const char *code, uint64_t bytes, const uint32_t block[3]) {437 uint64_t shader = 0;438 unsigned free = 64;439 for (unsigned i = 0; i < 64; i++) {440 if (!kernels[i].code) { if (free == 64) free = i; continue; }441 if (kernels[i].code != code) continue;442 shader = kernels[i].shader;443 if (!memcmp(kernels[i].block, block, sizeof kernels[i].block)) return (int64_t)kernels[i].pipeline;444 }445 if (free == 64) return -ENOSPC;446 dolly_gpu_begin(&g);447 if (!shader) {448 shader = next++;449 char *record = dolly_gpu_record(&g, DOLLY_GPU_CREATE_SHADER, 24 + bytes);450 uint32_t length = (uint32_t)bytes;451 memcpy(record + 8, &shader, 8);452 memcpy(record + 16, &length, 4);453 memcpy(record + 24, code, bytes);454 }455 /* COMPUTE_CONSTANTS: the entry "kernel", then block_x, block_y, block_z. */456 uint64_t id = next++;457 char *record = dolly_gpu_record(&g, DOLLY_GPU_COMPUTE_CONSTANTS, 40 + 3 * 24);458 uint32_t entry = 6, count = 3;459 memcpy(record + 8, &id, 8);460 memcpy(record + 16, &shader, 8);461 memcpy(record + 24, &entry, 4);462 memcpy(record + 28, &count, 4);463 memcpy(record + 32, "kernel", 6);464 for (unsigned axis = 0; axis < 3; axis++) {465 char *constant = record + 40 + axis * 24;466 uint32_t name = 7;467 double value = block[axis];468 memcpy(constant, &name, 4);469 memcpy(constant + 8, &value, 8);470 memcpy(constant + 16, "block_x", 7);471 constant[22] += (char)axis;472 }473 int64_t error = done();474 if (error) return error;475 kernels[free].code = code;476 kernels[free].shader = shader;477 kernels[free].pipeline = id;478 memcpy(kernels[free].block, block, sizeof kernels[free].block);479 return (int64_t)id;480 }481 482 /* A kernel's log: the words its records take, then each record's thread,483 * its length in bytes and its text. One that does not fit has no text. */484 enum { LOG_WORDS = 16 * 1024 };485 static const char log_binding[] = ") var<storage, read_write> log:";486 static uint64_t log_buffer;487 488 static int by_thread(const void *a, const void *b) {489 const uint32_t *left = *(const uint32_t *const *)a, *right = *(const uint32_t *const *)b;490 return left[0] != right[0] ? (left[0] < right[0] ? -1 : 1) : (left > right) - (left < right);491 }492 493 /* Writes what the kernel that has just run printed, thread after thread. */494 static int64_t print_log(void) {495 static uint32_t words[LOG_WORDS];496 static const uint32_t *records[LOG_WORDS / 2];497 int64_t error = mojo_gpu_read(log_buffer, 0, words, sizeof words);498 if (error) return error;499 unsigned count = 0, lost = 0;500 for (uint64_t at = 1; at <= words[0] && at + 2 <= LOG_WORDS; at += 2 + ((uint64_t)words[at + 1] + 3) / 4) {501 if (at + 2 + ((uint64_t)words[at + 1] + 3) / 4 <= LOG_WORDS) records[count++] = words + at;502 else lost = 1;503 }504 qsort(records, count, sizeof *records, by_thread);505 for (unsigned i = 0; i < count; i++) fwrite(records[i] + 2, 1, records[i][1], stdout);506 fflush(stdout);507 if (lost || words[0] > LOG_WORDS - 1) fputs("mojo: a kernel printed more than its 64 KiB of log\n", stderr);508 return 0;509 }510 511 int64_t mojo_gpu_launch(const char *code, uint64_t bytes, const uint32_t grid[3], const uint32_t block[3],512 const uint64_t *buffers, const uint64_t *sizes, uint32_t buffer_count,513 const uint32_t *slots, uint32_t slot_count) {514 int logs = memmem(code, bytes, log_binding, sizeof log_binding - 1) != 0;515 if (buffer_count + !!slot_count + logs > DOLLY_GPU_MAX_BINDINGS) return -E2BIG;516 int64_t compute = pipeline(code, bytes, block);517 if (compute < 0) return compute;518 uint64_t bound[DOLLY_GPU_MAX_BINDINGS], bound_sizes[DOLLY_GPU_MAX_BINDINGS], arguments = 0;519 memcpy(bound, buffers, buffer_count * 8);520 /* A kernel reads whole 32-bit words, also of a buffer of bytes. */521 for (unsigned i = 0; i < buffer_count; i++) bound_sizes[i] = (sizes[i] + 3) & ~(uint64_t)3;522 dolly_gpu_begin(&g);523 if (slot_count) {524 arguments = next++;525 dolly_gpu_buffer(&g, arguments, slot_count * 4, STORAGE);526 dolly_gpu_write(&g, arguments, slots, slot_count * 4);527 bound[buffer_count] = arguments;528 bound_sizes[buffer_count++] = slot_count * 4;529 }530 if (logs) {531 uint32_t empty = 0;532 if (!log_buffer) dolly_gpu_buffer(&g, log_buffer = next++, LOG_WORDS * 4, STORAGE);533 dolly_gpu_write(&g, log_buffer, &empty, 4);534 bound[buffer_count] = log_buffer;535 bound_sizes[buffer_count++] = LOG_WORDS * 4;536 }537 uint64_t group = next++;538 dolly_gpu_group(&g, group, (uint64_t)compute, buffer_count, bound, bound_sizes);539 char *record = dolly_gpu_record(&g, DOLLY_GPU_COMPUTE, 40);540 memcpy(record + 8, &compute, 8);541 memcpy(record + 16, &group, 8);542 memcpy(record + 24, grid, 12);543 dolly_gpu_submit(&g);544 int64_t error = done();545 if (!error && logs) error = print_log();546 if (error) return error;547 error = mojo_gpu_release(group);548 return error || !arguments ? error : mojo_gpu_release(arguments);549 }550 551 int64_t mojo_gpu_synchronize(void) { return dolly_gpu_wait(&g) < 0 ? -errno : 0; }552 553 /* What the device admits of a launch: the threads of a block, its x, y and554 * z, the workgroups of a dimension, the storage buffers of a kernel. */555 int64_t mojo_gpu_limits(uint32_t limits[6]) {556 static uint32_t known[6];557 if (!known[0]) {558 if (dolly_gpu_capabilities(&g) < 72) return errno ? -errno : -EIO;559 memcpy(known, g.reply + 44, 20);560 memcpy(known + 5, g.reply + 68, 4);561 }562 memcpy(limits, known, sizeof known);563 return 0;564 }565566SLOP cc -O2 -Wall -Wextra -Werror -c /tmp/mojo-runtime/runtime.c -o /tmp/mojo-runtime/runtime.o567SLOP cc -O2 -Wall -Wextra -Werror -c /tmp/mojo-runtime/gpu.c -o /tmp/mojo-runtime/gpu.o568SLOP ar rcs /usr/lib/libmojo-rt.a /tmp/mojo-runtime/runtime.o569SLOP ar rcs /usr/lib/libmojo-gpu.a /tmp/mojo-runtime/gpu.o570SLOP rm -rf /tmp/mojo /tmp/mojo.tar.gz /tmp/mojo-runtime571572573574575576577FILE /etc/modular/modular.cfg578 [mojo-max]579 package_root = /usr580 import_path = /usr/lib/mojo581 compilerrt_path = /usr/lib/libmojo-rt.a582 shared_libs = -lmojo-gpu,-ldolly-gpu583EXPORTS TOOL mojo584EXPORTS LIB mojo-rt /usr/lib/libmojo-rt.a585EXPORTS LIB mojo-gpu /usr/lib/libmojo-gpu.a586EXPORTS FOLDER mojo-packages /usr/lib/mojo587EXPORTS FOLDER mojo-licenses /usr/share/licenses/mojo588FILE /usr/share/man/cat1/mojo.1589 mojo: the Mojo compiler built inside Dolly, with its standard library, the590 package gpu and the cc package it links with.591 592 mojo build NAME.mojo && ./NAME compiles a program for Dolly, then runs it593 mojo precompile -o P.mojoc DIR makes a package; -I finds it594 mojo doc, mojo demangle as upstream's595 596 mojo run and mojo NAME.mojo run a program through a JIT, which a WebAssembly597 process cannot do: they refuse and print the line above. mojo repl and mojo598 debug need LLDB, which is not built, and mojo format a formatter that is not599 installed. The only target is Dolly's (wasm64). A program that imports gpu600 runs where the image has gpu@0, as mojo-gpu does; elsewhere it is refused601 with status 126. The compiler runs on one thread.602