diff options
| author | 2025-03-11 22:55:14 +0330 | |
|---|---|---|
| committer | 2025-03-11 22:55:14 +0330 | |
| commit | 78041817542a06e817281d6614fc67be7c6196a5 (patch) | |
| tree | c9a660c837264e84e94fb08017d1fb8bfadf8a6a | |
| parent | Improve allocation fallback (diff) | |
Converted the build system to C! + Slight improvements
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 37 | ||||
| -rw-r--r-- | README.md | 26 | ||||
| -rw-r--r-- | build.c | 400 | ||||
| -rw-r--r-- | moonshine.h | 15 | ||||
| -rw-r--r-- | moonshine.m4 | 50 |
6 files changed, 438 insertions, 93 deletions
@@ -1,3 +1,4 @@ out.* -moonshine.m4.h +moonshine.expander.h perf.data* +builder diff --git a/Makefile b/Makefile deleted file mode 100644 index c69c23b..0000000 --- a/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -FLAGS = -Wno-incompatible-library-redeclaration -nostartfiles -nostdlib -SIZE_FLAGS = -Wl,--undefined=main -flto -Oz -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--strip-all -finline-hint-functions -fno-builtin -Wl,--print-gc-sections -fno-unwind-tables -fomit-frame-pointer -fno-asynchronous-unwind-tables -fno-ident -fvisibility=hidden -march=native -Wl,--build-id=none - -all: m4 c test - -clean: - rm -rf out.* - rm -rf *.m4.h - rm -rf perf.data* - -m4: - m4 moonshine.m4 > moonshine.m4.h - -c: - clang main.c -o out.bin $(FLAGS) -O3 - -test: - ./out.bin - -profile: - clang main.c -o out.bin.profile $(FLAGS) -O3 - perf record ./out.bin.profile - perf report > ./out.profile.txt - -release: - clang main.c -o out.bin.release $(FLAGS) $(SIZE_FLAGS) - -debug: m4 - clang main.c -o out.bin $(FLAGS) -g - lldb ./out.bin -o run - -install: - # make sure you have your CPATH configured to recognize `~/include/`! - cp moonshine.h ~/include/moonshine.h - cp moonshine.m4.h ~/include/moonshine.m4.h - -full: m4 c test install @@ -8,6 +8,32 @@ This library relies upon nothing but the C language itself, not even the standar Support for more operating systems and architectures is not part of the goals of this project, but it is a consideration to be kept in mind. That said, any contribution to expand support to other configurations are absolutely welcome. +## Usage guide +First, build the builder (yes, the build system and macro generator are written in no-libc C as well): +```sh +$ clang build.c -nostdlib -o builder +``` +Run it for a guide: +```sh +$ ./builder +``` +Build the macros: +```sh +$ ./builder macro +``` +Verify the test cases: +```sh +$ ./builder run +``` +Install moonshine: +```sh +$ ./builder install +``` +All-in-one quick install: +```sh +$ ./builder all +``` + ## Current features - Mark & Sweep garbage collector for responsible memory management (must be manually called, for now) - Easy array creation: `new(int)`, `new(char, 4)`, `fill(int, 2, 4, 5)`, `fill(string, "hello", "world")`, `single("string")`, `single(22.4)` @@ -0,0 +1,400 @@ +// The moonshine library build and installation script. + +// ----- This part is a bit of boilerplate to establish basic functionality and get to work. ----- + +#define syscall1(num) \ + ({ \ + long result; \ + __asm__ volatile("syscall" : "=a"(result) : "a"(num) : "rcx", "r11", "memory", "cc"); \ + result; \ + }) + +#define syscall2(num, arg1) \ + ({ \ + long result; \ + __asm__ volatile("syscall" : "=a"(result) : "a"(num), "D"(arg1) : "rcx", "r11", "memory", "cc"); \ + result; \ + }) + +#define syscall4(num, arg1, arg2, arg3) \ + ({ \ + long result; \ + __asm__ volatile("syscall" : "=a"(result) : "a"(num), "D"(arg1), "S"(arg2), "d"(arg3) : "rcx", "r11", "memory", "cc"); \ + result; \ + }) + +#define syscall5(num, arg1, arg2, arg3, arg4) \ + ({ \ + long result; \ + register long __arg4 __asm__("r10") = (long) (arg4); \ + __asm__ volatile("syscall" \ + : "=a"(result) \ + : "a"(num), "D"(arg1), "S"(arg2), "d"(arg3), "r"(__arg4) \ + : "rcx", "r11", "memory", "cc"); \ + result; \ + }) + +#define stdin 0 +#define stdout 1 +#define stderr 2 + +#define BUFFER_SIZE 4096 + +char stdout_buffer[ BUFFER_SIZE ]; +int stdout_buffer_index = 0; + +int flush(int fd) { + if (stdout_buffer_index == 0) return 0; + + int total_written = 0; + while (total_written < stdout_buffer_index) { + int result = syscall4(1, fd, stdout_buffer + total_written, stdout_buffer_index - total_written); + if (result < 0) return -1; + total_written += result; + } + stdout_buffer_index = 0; + return total_written; +} + +int write(const char *ptr, const int size, const int fd) { + if (size <= 0) return 0; + + if (fd == 1) { + for (int i = 0; i < size; i++) { + if (ptr[ i ] == 0) return flush(fd); + stdout_buffer[ stdout_buffer_index++ ] = ptr[ i ]; + if (stdout_buffer_index == BUFFER_SIZE || ptr[ i ] == '\n') { + int result = flush(fd); + if (result < 0) return -1; + } + } + return size; + } else { + int total_written = 0; + while (total_written < size) { + int result = syscall4(1, fd, (void *) (ptr + total_written), size - total_written); + if (result < 0) { return -1; } + total_written += result; + } + return total_written; + } +} + +#define staputs(txt) \ + ({ \ + const char __txt[] = "" txt; \ + write(__txt, sizeof(__txt), stdout); \ + }) +#define puts(txt) \ + ({ \ + const char *__txt = txt; \ + write(__txt, strlen(__txt), stdout); \ + }) + +__attribute__((diagnose_as_builtin(__builtin_strlen, 1))) unsigned long strlen(const char *str) { + unsigned long len = 0; + while (str[ len ] != '\0') len++; + return len; +} + +void exit(const int exit_code) { + const unsigned long syscall_number = 60; + + flush(stdout); + (void) syscall2(syscall_number, exit_code); + + __builtin_unreachable(); +} + +int fork() { + const unsigned long syscall_number = 57; + const int pid = syscall1(syscall_number); + + return pid; +} + +char **environ; + +[[noreturn]] __attribute__((used)) void __moonshine_builder_start(int argc, char **argv, char **envp) { + int exit_code; + + environ = envp; + + __asm__ __volatile__("mov %1, %%rdi \n" + "mov %2, %%rsi \n" + "call main \n" + "mov %%eax, %0 \n" + : "=r"(exit_code) + : "r"((long) argc), "r"(argv) + : "rdi", "rsi", "rax", "memory"); + + exit(exit_code); + __builtin_unreachable(); +} + +__attribute__((force_align_arg_pointer)) __attribute__((naked)) void _start(void) { + __asm__ volatile("mov (%rsp), %rdi \n" + "lea 8(%rsp), %rsi \n" + "mov %rdi, %rax \n" + "lea (,%rax,8), %rcx \n" + "add $16, %rcx \n" + "lea (%rsp, %rcx), %rdx \n" + "call __moonshine_builder_start \n" + "mov $60, %rax \n" + "xor %rdi, %rdi \n" + "syscall \n" + "hlt \n"); +} + +#define NULL ((void *) 0) + +int execl(const char *path, const char *arg0, const char *arg1, const char *arg2, char **envp) { + char *argv[] = { (char *) arg0, (char *) arg1, (char *) arg2, (char *) 0 }; + int ret = (int) syscall4(59, path, argv, envp); + return ret; +} + +int waitpid(int pid, int *status, int options) { return syscall5(61, pid, status, options, 0); } + +int system(const char *command) { + int pid; + int status; + + if (command == NULL) { return 1; } + + pid = fork(); + if (pid == -1) { return -1; } + if (pid == 0) { + execl("/bin/sh", "sh", "-c", command, environ); + exit(127); + } + waitpid(pid, &status, 0); + return status; +} + +int run(const char *command) { + staputs("> "); + puts(command); + staputs("\n"); + return system(command); +} + +__attribute__((diagnose_as_builtin(__builtin_memcpy, 1, 2, 3))) void * +memcpy(void *_dest, const void *_src, const unsigned long n) { + char *dest = _dest; + const char *src = _src; + + for (unsigned long i = 0; i < n; i++) dest[ i ] = src[ i ]; + return dest; +} + +__attribute__((diagnose_as_builtin(__builtin_strcmp, 1, 2))) int strcmp(const char *left, const char *right) { + while (*left && (*left == *right) && left++ && right++); + return (unsigned char) (*left) - (unsigned char) (*right); +} + +__attribute__((diagnose_as_builtin(__builtin_strncmp, 1, 2, 3))) int +strncmp(const char *left, const char *right, unsigned long n) { + while (n && *left && (*left == *right)) { + left++; + right++; + n--; + } + return (n == 0) ? 0 : ((char) (*left) - (char) (*right)); +} + +#define putchar(c) \ + ({ \ + char f = c; \ + write(&f, 1, stdout); \ + }) + +void puts_number(const long number, const unsigned char is_signed) { + char temp[ 20 ]; + int len = 0; + long n = number; + + if (is_signed && number < 0) { + putchar('-'); + n = -n; + } + + do { + temp[ len++ ] = '0' + (n % 10); + n /= 10; + } while (n > 0); + + for (int i = len - 1; i >= 0; i--) putchar(temp[ i ]); +} + +unsigned char eq(const char *l, const char *r) { return strcmp(l, r) == 0; } + +// ----- The macro processor starts here. ----- + +#define type(...) \ + ({ \ + staputs(__VA_ARGS__); \ + putchar('\n'); \ + }) + +void macro_define_part(char *name, int index, int count) { + staputs("#define EXPAND_"); + puts(name); + staputs("_"); + puts_number(index, 0); + staputs("(func, extra, x, ...) func(extra, "); + puts_number(count - index + 1, 0); + staputs(", x)"); + if(index == 1) { + putchar('\n'); + return; + } + staputs(" cat(dump_or_continue_0, __VA_OPT__(1))(EXPAND_"); + puts(name); + staputs("_"); + puts_number(index - 1, 0); + type("(func, extra, __VA_ARGS__))"); +} + +void macro_define_expander(char *name, int count) { + staputs("#define EXPAND_"); + puts(name); + staputs("(func, extra, ...) EXPAND_"); + puts(name); + staputs("_"); + puts_number(count, 0); + type("(func, extra, __VA_ARGS__)"); + + for (int i = 0; i < count; i++) { macro_define_part(name, count - i, count); } +} + +void macro_processor() { + type("#ifndef moonshine_m4_header\n" + "#define moonshine_m4_header\n" + "\n" + "// Autogenerated by the moonshine builder.\n" + "// If you wanna make changes, good luck :P\n"); + + type("#define p_cat(a, b) a##b\n" + "#define cat(a, b) p_cat(a, b)\n" + "#define cat3(a, b, c) cat(a, cat(b, c))\n" + "#define cat4(a, b, c, d) cat(cat(a, b), cat(c, d))\n"); + + type("#define dump_or_continue_0(...)\n" + "#define dump_or_continue_01(...) __VA_ARGS__\n"); + + macro_define_expander("general", 1024); + macro_define_expander("general2", 1024); + + type("\n#endif"); +} + +// ----- The real builder code starts here. ----- + +#define FLAGS "-Wno-incompatible-library-redeclaration -nostartfiles -nostdlib" +#define SIZE_FLAGS \ + "-Wl,--undefined=main -flto -Oz -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--strip-all " \ + "-finline-hint-functions -fno-builtin -Wl,--print-gc-sections -fno-unwind-tables -fomit-frame-pointer " \ + "-fno-asynchronous-unwind-tables -fno-ident -fvisibility=hidden -march=native -Wl,--build-id=none" + +unsigned char code_ran = 0; + +void run_dep_code() { + if (code_ran) return; + code_ran = 1; + run("clang main.c -o out.bin " FLAGS " -O3"); +} + +unsigned char macro_ran = 0; + +void run_dep_macro(char *name) { + if (macro_ran) return; + macro_ran = 1; + + int self_len = strlen(name); + char addon[] = " __internal_macro_processor__ > moonshine.expander.h"; + char self[ self_len + sizeof(addon) + 1 ]; + + memcpy(self, name, self_len); + memcpy(self + self_len, addon, sizeof(addon)); + self[ sizeof(self) - 1 ] = '\0'; + + run("rm -f moonshine.expander.h"); + system(self); +} + +unsigned char install_ran = 0; + +void run_dep_install() { + if (install_ran) return; + install_ran = 1; + + type("Make sure your CPATH environment variable is set to recognize ~/include/ as a valid header directory."); + type("Otherwise, clang will not recognize moonshine as a valid library."); + + run("cp moonshine.h ~/include/moonshine.h"); + run("cp moonshine.expander.h ~/include/moonshine.expander.h"); +} + +int main(int argc, char **argv) { + if (argc <= 1) { + staputs("Usage:\n "); + puts(argv[ 0 ]); + staputs( + " [ clean | macro | code | run | profile | debug | install ]+ | <all>\n\n" + "This is a bootstrapped lightweight make alternative designed specifically to work with the moonshine library.\n" + "Clean residue files with:\n$ "); + puts(argv[ 0 ]); + staputs(" clean\n" + "Run the tests with:\n$ "); + puts(argv[ 0 ]); + staputs(" run\n" + "Install the header files for use within your projects:\n$ "); + puts(argv[ 0 ]); + staputs(" install\n" + "Perform preprocessing and install moonshine:\n$ "); + puts(argv[ 0 ]); + staputs(" all\n"); + } + + int tracker = 0; + while (++tracker < argc) { + char *argument = argv[ tracker ]; + + if (eq(argument, "clean")) { + type("-- clean"); + run("rm -f out.*"); + run("rm -f *.expander.h"); + run("rm -f perf.data*"); + } else if (eq(argument, "macro")) { + type("-- macro"); + run_dep_macro(argv[ 0 ]); + } else if (eq(argument, "code")) { + type("-- code"); + run_dep_code(); + } else if (eq(argument, "run")) { + type("-- run"); + run_dep_code(); + run("./out.bin"); + } else if (eq(argument, "profile")) { + type("-- profile"); + run_dep_code(); + run("perf record ./out.bin"); + run("perf report > ./out.profile.txt"); + } else if (eq(argument, "debug")) { + type("-- debug"); + run("clang main.c -o out.bin $(FLAGS) -g"); + run("lldb ./out.bin -o run"); + } else if (eq(argument, "install")) { + type("-- install"); + run_dep_install(); + } else if (eq(argument, "all")) { + type("-- all"); + run_dep_macro(argv[ 0 ]); + run_dep_install(); + } else if (eq(argument, "__internal_macro_processor__")) { + macro_processor(); + exit(0); + } + } +} diff --git a/moonshine.h b/moonshine.h index bbd2c63..e3b7011 100644 --- a/moonshine.h +++ b/moonshine.h @@ -8,7 +8,7 @@ // -#include "moonshine.m4.h" +#include "moonshine.expander.h" #define fn_overload __attribute__((overloadable)) #define fn_line fn_overload flatten @@ -237,11 +237,12 @@ int errno = 0; write((char *) &y, 1, stdout); \ }) -__attribute__((diagnose_as_builtin(__builtin_memcpy, 1, 2, 3))) void memcpy(var _dest, car _src, const unsigned long n) { +__attribute__((diagnose_as_builtin(__builtin_memcpy, 1, 2, 3))) void *memcpy(var _dest, car _src, const unsigned long n) { string dest = _dest; ctring src = _src; for (u8 i = 0; i < n; i++) dest[ i ] = src[ i ]; + return dest; } #define stdin 0 @@ -271,6 +272,7 @@ int write(const char *ptr, const int size, const int fd) { if (fd == 1) { for (int i = 0; i < size; i++) { + if(ptr[i] == 0) return flush(fd); stdout_buffer[ stdout_buffer_index++ ] = ptr[ i ]; if (stdout_buffer_index == BUFFER_SIZE || ptr[ i ] == '\n') { int result = flush(fd); @@ -554,6 +556,7 @@ int fork() { void exit(const int exit_code) { const uint64_t syscall_number = 60; + flush(stdout); (void) syscall2(syscall_number, exit_code); __builtin_unreachable(); @@ -708,7 +711,7 @@ void realloc() __attribute__((deprecated(MOONSHINE_LEGACY_LIBC_WARNING, "remap") void free() __attribute__((deprecated(MOONSHINE_LEGACY_LIBC_WARNING, "release"))); #endif -__attribute__((diagnose_as_builtin(__builtin_strcmp, 1, 2))) byte strcmp(ctring left, ctring right) { +__attribute__((diagnose_as_builtin(__builtin_strcmp, 1, 2))) int strcmp(ctring left, ctring right) { while (*left && (*left == *right) && left++ && right++); return (byte) (*left) - (byte) (*right); } @@ -988,11 +991,13 @@ void mmapocator_clean_pages() { // TODO: Place page pointer and pointer size behind the allocated pointer +void collect_garbage(bool debug); __attribute__((diagnose_as_builtin(__builtin_malloc, 1))) __attribute__((malloc)) var mmapocator_alloc(const u8 len) [[clang::allocating]] { static int alloc_count = 0; if (unlikely(alloc_count++ == 4096)) { alloc_count = 0; + collect_garbage(false); mmapocator_clean_pages(); } @@ -1215,7 +1220,7 @@ var sbrkocator_alloc_new(u8 size) { return (void *) (new_block + 1); } -var sbrkocator_alloc(u8 size) { +__attribute__((malloc)) var sbrkocator_alloc(u8 size) { auto free_block = sbrkocator_find_free(size); if (free_block == NULL) return sbrkocator_alloc_new(size); @@ -2259,7 +2264,7 @@ __attribute__((force_align_arg_pointer)) __attribute__((naked)) void _start(void "lea 8(%rsp), %rsi \n" "mov %rdi, %rax \n" "lea (,%rax,8), %rcx \n" - "add $8, %rcx \n" + "add $16, %rcx \n" "lea (%rsp, %rcx), %rdx \n" "call __moonshine_start \n" "mov $60, %rax \n" diff --git a/moonshine.m4 b/moonshine.m4 deleted file mode 100644 index 5eb2615..0000000 --- a/moonshine.m4 +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef moonshine_m4_header -#define moonshine_m4_header - -dnl This is m4 preprocessor magic. -dnl You can safely ignore it, as it is toxic and radioactive to humans. -// # Autogenerated by m4 -// # Don't modify this, modify the main.m4 file instead. -dnl -dnl -changequote(`[', `]')dnl -define([set], defn([define]))dnl -undefine([define])dnl - -#define p_cat(a, b) a##b -#define cat(a, b) p_cat(a, b) -#define cat3(a, b, c) cat(a, cat(b, c)) -#define cat4(a, b, c, d) cat(cat(a, b), cat(c, d)) - -#define dump_or_continue_0(...) -#define dump_or_continue_01(...) __VA_ARGS__ - -set([_expand], [#define EXPAND_$3_$2(func, extra, x, ...) func(extra, $4, x) cat(dump_or_continue_0, __VA_OPT__(1))(EXPAND_$3_$1(func, extra, __VA_ARGS__))])dnl -set([expand], [ifelse([$1], [1], [#define EXPAND_$2_$1(func, extra, x, ...) func(extra, $3, x)], [_expand(decr($1), $1, $2, $3)])])dnl -dnl -set([build_expander], [ifelse([$1], [0], [], [expand($1, $2, $3) -build_expander(decr($1), $2, incr($3))dnl -])])dnl -set([define_expander], [#define EXPAND_$2(func, extra, ...) EXPAND_$2_$1(func, extra, __VA_ARGS__) -build_expander($1, $2, [1])])dnl -dnl -dnl -set([build_array], [ifelse([$1], [0], [$2], [$2,\ -build_array(decr($1), incr($2))])])dnl -set([define_array], [#define really_long_array \ -build_array(decr($1), 1)])dnl -dnl -dnl -dnl -dnl Normally, I only need two of these to work. But if you want to nest more, activate the rest! Make sure to run `make m4` when you change this file. -define_expander([1024], general) -define_expander([1024], general2) -dnl Activate these if needed. -dnl define_expander([512], general3) -dnl define_expander([512], general4) -dnl define_expander([512], general5) -dnl define_expander([512], general6) -dnl define_expander([512], general7) -dnl define_expander([512], general8) - -#endif |
