From 7f5d765c929a4dc2deddb7b68a41a3a841940837 Mon Sep 17 00:00:00 2001 From: Mel Date: Thu, 22 Jan 2026 03:55:02 +0100 Subject: LLVM clang compiler backend Signed-off-by: Mel --- boot/build.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 boot/build.c (limited to 'boot/build.c') 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. + * + * 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, + }; +} -- cgit 1.4.1