aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile37
-rw-r--r--README.md26
-rw-r--r--build.c400
-rw-r--r--moonshine.h15
-rw-r--r--moonshine.m450
6 files changed, 438 insertions, 93 deletions
diff --git a/.gitignore b/.gitignore
index dd60ed0..269e4ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/README.md b/README.md
index c776252..2df30b8 100644
--- a/README.md
+++ b/README.md
@@ -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)`
diff --git a/build.c b/build.c
new file mode 100644
index 0000000..ec6f0c3
--- /dev/null
+++ b/build.c
@@ -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