diff options
| author | Mel <mel@rnrd.eu> | 2026-01-22 03:55:02 +0100 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-01-22 03:55:02 +0100 |
| commit | 7f5d765c929a4dc2deddb7b68a41a3a841940837 (patch) | |
| tree | c442166ede9f6b4c3a82621a39d754dde8c407ac /boot/build.c | |
| parent | 30b04e4b2a90981570ae04095aeccd746ccdea6a (diff) | |
| download | catskill-7f5d765c929a4dc2deddb7b68a41a3a841940837.tar.zst catskill-7f5d765c929a4dc2deddb7b68a41a3a841940837.zip | |
LLVM clang compiler backend
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/build.c')
| -rw-r--r-- | boot/build.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/boot/build.c b/boot/build.c new file mode 100644 index 0000000..5d902d6 --- /dev/null +++ b/boot/build.c @@ -0,0 +1,152 @@ +/* + * a simple routine to call out to a proper + * compiler with our c source artifacts produced + * by our bootstrapping transpiler to turn them + * into real executable code. + * currently we call out directly to the llvm-based + * `clang` tool found in the system path to do + * the real heavy lifting for us. :) + * the catskill compiler does have an eventual goal + * to achieve full self-hosting with real assembler + * and linker implementations, however that is not + * of interest to our bootstrap phase. + * + * Copyright (c) 2026, Mel G. <mel@rnrd.eu> + * + * SPDX-License-Identifier: MPL-2.0 + */ + +#pragma once + +#include "catboot.h" + +#ifndef MUSL_LIB +#error "MUSL_LIB not defined" +#endif + +#ifndef MUSL_DEV +#error "MUSL_DEV not defined" +#endif + +const ascii* musl_lib = MUSL_LIB; +const ascii* musl_dev = MUSL_DEV; + +struct Compiler_Command_Result +{ + integer exit_code; + struct String log; +}; + +struct Compiler_Command_Result +run_compiler_command(const ascii* command) +{ + log_debug("running backend command: %s\n", command); + + FILE* command_pipe = popen(command, "r"); + if (!command_pipe) return (struct Compiler_Command_Result){ .exit_code = -1 }; + + uint bytes_read; + ascii log_buffer[8192] = { 0 }; + + while ((bytes_read = fread(log_buffer, 1, ARRAY_SIZE(log_buffer), command_pipe)) > 0) { + if (bytes_read >= ARRAY_SIZE(log_buffer)) { + log_error("compiler output surpassed maximum output length... truncating to %lu bytes\n", ARRAY_SIZE(log_buffer) - 1); + break; + } + } + + integer exit_code = pclose(command_pipe); + struct String log = string_from_c_string(log_buffer); + + return (struct Compiler_Command_Result){ .exit_code = exit_code, .log = log }; +} + +struct Build_Result +{ + bool success; + + integer compiler_exit_code; + struct String compiler_log; + struct String output_path; +}; + +struct Build_Result +build_executable(struct String build_path) +{ + + struct String source_path = string_append_c_str(build_path, "/input.c"); + struct String output_path = string_append_c_str(build_path, "/output"); + + ascii* compiler = "clang"; + + log_debug("current backend: %s\n", compiler); + + // TODO: right now we always run the compiler from this source directory, + // so we can refer to the runtime libraries by a relative path "./boot/runtime/", + // but in the future we should embed these files into this compiler, and write + // them out into the build directory. + + // probably too many flags for our purposes, but we kind of want + // this simple bootstrapping output to be the eventual target + // of our self-hosted compiler stack, so we avoid anything fancy + // for an easier target to hit. + const ascii* arguments[] = { + compiler, + + // high-level compiler behavior + "-O0", + "-g", + "-std=c99", + + // low-level compiler settings, which we optimize to generate + // extremely simple and human-readable assembly in our final + // executable. we want it to feel almost hand-written. + "-fno-omit-frame-pointer", + "-fno-stack-protector", + "-fno-plt", + "-fno-builtin", + "-fno-inline", + "-fno-common", + "-fno-ident", + "-fno-exceptions", + "-fno-asynchronous-unwind-tables", // we rely on frame pointer instead + + // output options, for integrating clang output into ours + "-fno-color-diagnostics", + "-Wall", + "-Wextra", // with -w flag or on error + + // linker and header options, static compilation w/ musl + "-static", + "-nostdlib", + "-I./boot/runtime/", + "-isystem", + MUSL_DEV "/include", + // linking must follow this exact order + MUSL_LIB "/lib/crt1.o", + MUSL_LIB "/lib/crti.o", + "./boot/runtime/stubs.c", // stub out some software float implementations + string_c_str(source_path), + MUSL_LIB "/lib/libc.a", + MUSL_LIB "/lib/crtn.o", + + "-o", + string_c_str(output_path), + }; + + // append all flags to single command + ascii command[1024] = { 0 }; + for (uint i = 0; i < ARRAY_SIZE(arguments); ++i) { + strcat(command, arguments[i]); + strcat(command, " "); + } + + struct Compiler_Command_Result result = run_compiler_command(command); + + return (struct Build_Result){ + .success = result.exit_code == 0, + .compiler_exit_code = result.exit_code, + .compiler_log = result.log, + .output_path = output_path, + }; +} |
