NAME=ccera.a
-SRCS=src/arc.c src/defer.c src/eval.c src/jmp.c src/panic.c src/value_destroy.c src/value_get.c src/value_lifetime.c src/value_new.c src/value_readf.c
+SRCS=src/arc.c src/defer.c src/eval.c src/framealloc.c src/panic.c src/value_destroy.c src/value_get.c src/value_lifetime.c src/value_new.c src/value_readf.c
-HEADERS=src/ccera.h src/defer.h src/jmp.h src/panic.h
+HEADERS=src/align.h src/ccera.h src/defer.h src/framealloc.h src/jmp.h src/panic.h src/return_patch.h
BUILDDIR=.build
#CFLAGS += -O3
#CFLAGS += -flto -ffat-lto-objects
-CC=clang
+#CC=clang
+CC=gcc
AR=ar
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* align.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2026/05/12 10:03:30 by agilliar #+# #+# */
+/* Updated: 2026/05/12 13:36:28 by agilliar ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#ifndef ALIGN_H
+# define ALIGN_H
+
+# include <stdint.h>
+
+# ifdef __builtin_align_up
+
+__attribute__((always_inline))
+static inline void *align_up(void *p, uintptr_t align)
+{
+ return (__builtin_align_up(p, align));
+}
+
+# else
+
+__attribute__((always_inline))
+static inline void *align_up(void *p, uintptr_t align)
+{
+ uintptr_t n;
+
+ n = (intptr_t) p;
+ if (n % align)
+ n = ((n / align) + n % align) * align;
+ n -= (intptr_t) p;
+ p = &((char *) p)[n];
+ return (p);
+}
+
+# endif
+
+#endif
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/04/29 16:08:57 by agilliar #+# #+# */
-/* Updated: 2026/05/10 22:11:53 by agilliar ### ########.fr */
+/* Updated: 2026/05/11 19:26:32 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
# include "jmp.h"
# include "defer.h"
# include "panic.h"
+# include "framealloc.h"
# if UINTPTR_MAX != 0xFFFFFFFFFFFFFFFF
# error "Platform's pointers are not 64 bits"
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/05 17:31:36 by agilliar #+# #+# */
-/* Updated: 2026/05/11 04:13:54 by agilliar ### ########.fr */
+/* Updated: 2026/05/12 14:02:39 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include "defer.h"
-#include <assert.h>
-static_assert((sizeof(t_defer_frame)) == 32);
-#define T_DEFER_FRAME_SIZE "32"
-
-static_assert((alignof(t_defer_frame)) == 8);
-#define T_DEFER_FRAME_ALIGN "8"
-
-asm (
- ".globl _defer_store\n"
- ".section .tbss,\"awT\",@nobits\n"
- ".align " T_DEFER_FRAME_ALIGN "\n"
- ".type _defer_store, @object\n"
- ".size _defer_store, " T_DEFER_FRAME_SIZE "\n"
- "_defer_store:\n"
- ".zero " T_DEFER_FRAME_SIZE "\n"
- ".text\n"
- );
-
-// TODO: impl
-//
-//typedef struct s_defer_frame
-//{
-// void *ret_ptr;
-// void *stack_ptr;
-// struct s_defer_frame *prev;
-// t_defer *frame;
-//} t_defer_frame;
-
-__attribute__((naked))
-void defer_unpatch_exit(void)
+t_defer_frame *defer_store(void)
{
- asm (
- "movq %%rax, %%rdi\n"
- "movq %%rdx, %%rsi\n"
- "movq %%rsp, %%rdx\n"
- "movq %%fs:_defer_store@TPOFF+%c0, %%rsp\n"
- "pushq %%rdi\n"
- "pushq %%rsi\n"
- "pushq %%rdx\n"
- "pushq %%fs:_defer_store@TPOFF+%c1\n"
- "callq defer_pop_frame\n"
- "popq %%rdi\n"
- "popq %%rsi\n"
- "popq %%rdx\n"
- "popq %%rax\n"
- "movq %%rsi, %%rsp\n"
- "movq %%rdi, -8(%%rsp)\n"
- "jmpq *%%rdi\n"
- ::
- "n" (offsetof(t_defer_frame, stack_ptr)),
- "n" (offsetof(t_defer_frame, ret_ptr)));
-}
+ static __thread t_defer_frame store;
-// TODO: impl
-//typedef struct s_defer
-//{
-// struct s_defer *prev;
-// void (*f)(void *);
-// void *dat;
-// bool err;
-//} t_defer;
+ return (&store);
+}
-__attribute__((used))
-void defer_pop_frame(bool err)
+void defer_pop(bool err)
{
t_defer *curr;
if (defer_store()->prev)
*defer_store() = *defer_store()->prev;
}
+
+__attribute__((used))
+void *defer_pop_ret(void)
+{
+ void *ret;
+
+ ret = defer_store()->ret_ptr;
+ defer_pop(false);
+ return (ret);
+}
+
+__attribute__((naked))
+void defer_pop_shim(void)
+{
+ asm (
+ "pushq %rax\n"
+ "pushq %rdx\n"
+ "callq defer_pop_ret\n"
+ "movq %rax, %rsi\n"
+ "popq %rdx\n"
+ "popq %rax\n"
+ "pushq %rsi\n"
+ "ret\n"
+ );
+}
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/10 22:08:53 by agilliar #+# #+# */
-/* Updated: 2026/05/11 04:46:59 by agilliar ### ########.fr */
+/* Updated: 2026/05/12 14:01:36 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
# define DEFER_H
# include <stddef.h>
+# include "framealloc.h"
+# include "return_patch.h"
typedef struct s_defer
{
typedef struct s_defer_frame
{
void *ret_ptr;
- void *stack_ptr;
+ void *frame_ptr;
struct s_defer_frame *prev;
t_defer *frame;
} t_defer_frame;
-__attribute__((always_inline))
-static inline t_defer_frame *defer_store(void)
-{
- extern __thread t_defer_frame _defer_store;
-
- return (&_defer_store);
-}
-void defer_unpatch_exit(void)
+t_defer_frame *defer_store(void);
+void defer_pop(bool err);
+void defer_pop_shim(void)
__attribute__((naked));
-// Store current rsp + red zone, aligned down to stack align
-// No clang 22, so no __builtin_stack_address
-__attribute__((always_inline))
-static inline void defer_stack_update(void)
-{
- void *addr;
-
- asm ("leaq -128(%%rsp), %0\n" : "=r" (addr));
- //defer_store()->stack_ptr = __builtin_align_down(addr, 16);
- defer_store()->stack_ptr = (void *)((unsigned long long )addr & ~16ull);
-}
-
__attribute__((always_inline))
static inline void defer_patch(void)
{
- void *ret_ptr;
t_defer_frame *prev;
t_defer_frame *curr;
+ void *ret;
- ret_ptr = __builtin_return_address(0);
- if (&defer_unpatch_exit != ret_ptr)
+ if (defer_store()->frame_ptr != __builtin_frame_address(0))
{
- asm ("movq %0, 8(%%rbp)\n" : : "r" (&defer_unpatch_exit));
- prev = __builtin_alloca(sizeof(t_defer_frame));
+ ret = __builtin_return_address(0);
+ return_patch(&defer_pop_shim);
+ prev = cera_alloca_align(sizeof(t_defer_frame), alignof(t_defer_frame));
curr = defer_store();
*prev = *curr;
- curr->ret_ptr = ret_ptr;
+ curr->ret_ptr = ret;
curr->frame = NULL;
+ curr->prev = prev;
+ defer_store()->frame_ptr = __builtin_frame_address(0);
}
- defer_stack_update();
- ret_ptr = __builtin_return_address(0);
- __builtin_assume(&defer_unpatch_exit == ret_ptr);
}
__attribute__((always_inline))
{
t_defer *curr;
- curr = __builtin_alloca(sizeof(t_defer));
+ curr = cera_alloca_align(sizeof(t_defer), alignof(t_defer));
defer_patch();
curr->prev = defer_store()->frame;
curr->f = f;
{
t_defer *curr;
- curr = __builtin_alloca(sizeof(t_defer));
+ curr = cera_alloca_align(sizeof(t_defer), alignof(t_defer));
defer_patch();
curr->prev = defer_store()->frame;
curr->f = f;
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* framealloc.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2026/05/11 19:22:02 by agilliar #+# #+# */
+/* Updated: 2026/05/12 14:01:24 by agilliar ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#include "framealloc.h"
+
+t_framealloc *framealloc_store(void)
+{
+ static __thread t_framealloc store;
+
+ return (&store);
+}
+
+void framealloc_pop(void)
+{
+ t_framealloc *store;
+
+ store = framealloc_store();
+ store->frame = *store->frame.prev;
+}
+
+__attribute__((used))
+void *framealloc_pop_ret(void)
+{
+ void *ret_ptr;
+
+ ret_ptr = framealloc_store()->frame.ret_ptr;
+ framealloc_pop();
+ return (ret_ptr);
+}
+
+__attribute__((naked))
+void framealloc_pop_shim(void)
+{
+ asm (
+ "pushq %rax\n"
+ "pushq %rdx\n"
+ "callq framealloc_pop_ret\n"
+ "movq %rax, %rsi\n"
+ "popq %rdx\n"
+ "popq %rax\n"
+ "pushq %rsi\n"
+ "retq\n"
+ );
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* framealloc.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2026/05/11 19:17:56 by agilliar #+# #+# */
+/* Updated: 2026/05/12 13:35:45 by agilliar ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#ifndef FRAMEALLOC_H
+# define FRAMEALLOC_H
+
+# include <stdint.h>
+# include "return_patch.h"
+# include "align.h"
+
+# ifndef FRAMEALLOC_SIZE
+# define FRAMEALLOC_SIZE 32768
+# endif
+
+typedef struct s_framealloc_frame
+{
+ uintptr_t used;
+ struct s_framealloc_frame *prev;
+ void *ret_ptr;
+ void *frame_ptr;
+} t_framealloc_frame;
+
+typedef struct s_framealloc
+{
+ t_framealloc_frame frame;
+ char buf[FRAMEALLOC_SIZE];
+} t_framealloc;
+
+t_framealloc *framealloc_store(void);
+void framealloc_pop_shim(void)
+ __attribute__((naked));
+void framealloc_pop(void);
+
+__attribute__((always_inline, malloc, alloc_size(1), alloc_align(2)))
+static inline void *cera_alloca_align_nopatch(uintptr_t len, uintptr_t align)
+{
+ t_framealloc *store;
+ void *res;
+
+ store = framealloc_store();
+ res = &store->buf[store->frame.used];
+ store->frame.used += ((uintptr_t)(align_up(res, align) - res))
+ + len;
+ res = align_up(res, align);
+ return (res);
+}
+
+__attribute__((always_inline))
+static inline void cera_alloca_patch(void)
+{
+ t_framealloc *store;
+ t_framealloc_frame prev_frame;
+ t_framealloc_frame *frame_p;
+ void *ret;
+
+ store = framealloc_store();
+ if (store->frame.frame_ptr != __builtin_frame_address(0))
+ {
+ ret = __builtin_return_address(0);
+ return_patch(&framealloc_pop_shim);
+ prev_frame = store->frame;
+ frame_p = cera_alloca_align_nopatch(sizeof(t_framealloc_frame),
+ alignof(t_framealloc_frame));
+ *frame_p = prev_frame;
+ store->frame.prev = frame_p;
+ store->frame.ret_ptr = ret;
+ store->frame.frame_ptr = __builtin_frame_address(0);
+ }
+}
+
+__attribute__((always_inline, malloc, alloc_size(1), alloc_align(2)))
+static inline void *cera_alloca_align(uintptr_t len, uintptr_t align)
+{
+ cera_alloca_patch();
+ return (cera_alloca_align_nopatch(len, align));
+}
+
+#endif
+++ /dev/null
-/* ************************************************************************** */
-/* */
-/* ::: :::::::: */
-/* jmp.c :+: :+: :+: */
-/* +:+ +:+ +:+ */
-/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
-/* +#+#+#+#+#+ +#+ */
-/* Created: 2026/05/10 06:19:18 by agilliar #+# #+# */
-/* Updated: 2026/05/11 04:24:42 by agilliar ### ########.fr */
-/* */
-/* ************************************************************************** */
-
-#include <stddef.h>
-#include "jmp.h"
-
-__attribute__((returns_twice, naked))
-bool cera_setjmp(void *buf[2])
-{
- asm (
- "movq %%rbp, %c0(%%rdi)\n"
- "movq %%rsp, %c1(%%rdi)\n"
- "movq 0(%%rsp), %%rax\n"
- "movq %%rax, %c2(%%rdi)\n"
- "xorq %%rax, %%rax\n"
- "retq\n"
- ::
- "n" (offsetof(t_jmp, base)),
- "n" (offsetof(t_jmp, top)),
- "n" (offsetof(t_jmp, ret)));
-}
-
-__attribute__((noreturn, naked))
-void cera_longjmp(void *buf[2])
-{
- asm (
- "movq %c0(%%rdi), %%rbp\n"
- "movq %c1(%%rdi), %%rsp\n"
- "movq %c2(%%rdi), %%rax\n"
- "movq %%rax, 0(%%rsp)\n"
- "movl $1, %%eax\n"
- "retq\n"
- ::
- "n" (offsetof(t_jmp, base)),
- "n" (offsetof(t_jmp, top)),
- "n" (offsetof(t_jmp, ret)));
-}
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/10 22:08:02 by agilliar #+# #+# */
-/* Updated: 2026/05/11 01:43:42 by agilliar ### ########.fr */
+/* Updated: 2026/05/12 13:12:47 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef JMP_H
# define JMP_H
-// We are storing the return address so as to always return to the right
-// function, as a defer might have patched the return address of any later
-// stack frames
-// Clang's impl would doom us
-typedef struct s_jmp
-{
- void *base;
- void *top;
- void *ret;
-} t_jmp;
+# include <stdint.h>
-bool cera_setjmp(void *buf[2])
- __attribute__((returns_twice, naked));
-
-void cera_longjmp(void *buf[2])
- __attribute__((noreturn, naked));
+typedef intptr_t t_jmp[3];
#endif
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/05 15:38:33 by agilliar #+# #+# */
-/* Updated: 2026/05/11 02:26:13 by agilliar ### ########.fr */
+/* Updated: 2026/05/12 13:13:03 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include "ccera.h"
-/*
t_panic_info *panic_store(void)
{
static __thread t_panic_info store;
void *catch(void *f, void *dat)
{
void *res;
- t_defer defer_store;
t_panic_info prev;
+ cera_alloca_patch();
+ defer_patch();
prev = *panic_store();
- panic_store()->defer = &defer_store;
- defer_new(&defer_store);
- if (__builtin_setjmp(panic_store()->jmp))
+ panic_store()->frame_ptr = __builtin_frame_address(0);
+ if (__builtin_setjmp(&panic_store()->jmp))
{
res = panic_store()->data;
*panic_store() = prev;
__attribute__((noreturn, nonnull(1)))
void panic(void *err)
{
- defer_exit(panic_store()->defer);
+ while (defer_store()->frame_ptr != panic_store()->frame_ptr)
+ defer_pop(true);
+ while (framealloc_store()->frame.frame_ptr != panic_store()->frame_ptr)
+ framealloc_pop();
panic_store()->data = err;
- __builtin_longjmp(panic_store()->jmp, 1);
+ __builtin_longjmp(&panic_store()->jmp, 1);
}
bool panicking(void)
{
return (panic_store()->data != NULL);
}
-*/
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/10 22:11:10 by agilliar #+# #+# */
-/* Updated: 2026/05/10 22:11:44 by agilliar ### ########.fr */
+/* Updated: 2026/05/12 12:16:56 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef PANIC_H
# define PANIC_H
+# include "jmp.h"
+
typedef struct s_panic_info
{
void *data;
- void *jmp[2];
- t_defer *defer;
+ t_jmp jmp;
+ void *frame_ptr;
} t_panic_info;
-void *catch(void *f, void *dat);
-void panic(void *err)
- __attribute__((noreturn, nonnull(1)));
-bool panicking(void);
+t_panic_info *panic_store(void);
+
+void panic(void *err)
+ __attribute__((noreturn, nonnull(1)));
+void *catch(void *f, void *dat);
+bool panicking(void);
#endif
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* return_patch.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2026/05/11 20:08:23 by agilliar #+# #+# */
+/* Updated: 2026/05/12 08:54:17 by agilliar ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+#ifndef RETURN_PATCH_H
+# define RETURN_PATCH_H
+
+__attribute__((always_inline))
+static inline void return_patch(void *p)
+{
+ void **frame_addr;
+
+ frame_addr = __builtin_frame_address(0);
+ frame_addr[1] = p;
+}
+
+#endif
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/04/30 17:19:58 by agilliar #+# #+# */
-/* Updated: 2026/05/11 04:12:03 by agilliar ### ########.fr */
+/* Updated: 2026/05/12 14:07:55 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include <stdio.h>
#include <stdlib.h>
-/*
-void main2(void *s)
+void main2(char *orig)
{
- t_defer defer_store __attribute__((cleanup(defer_exit)));
- defer_new(&defer_store);
-
- t_value arg = value_bytes_new(strlen(s) + 1);
- defer(value_destroy, &arg);
- panic("A");
- t_value state = value_list_new(0);
- defer(value_destroy, &state);
- //panic("B");
- t_value idx = value_int_new(0);
- t_value fn = value_list_new(2);
- defer(value_destroy, &fn);
- //panic("C");
- t_value expr = value_list_new(2);
- defer(value_destroy, &expr);
- //panic("D");
- defer_re(&defer_store);
- value_list_get(fn)->list[0] = state;
- value_list_get(fn)->list[1] = idx;
- value_list_get(expr)->list[0] = fn;
- value_list_get(expr)->list[1] = arg;
- defer(value_destroy, &expr);
- //panic("E");
- t_value val = value_copy(expr);
- errdefer(value_destroy, &val);
- //panic("F");
-}
-
-int main(void)
-{
- char *err = catch(main2, "hello cera");
- if (err)
- {
- printf("%s\n", err);
- return (1);
- }
+ char *s = malloc(strlen(orig) + 1);
+ defer(free, s);
+ char *s2 = malloc(strlen(orig) + 1);
+ defer(free, s2);
+ panic("error!");
}
-*/
int main(void)
{
+ char *msg;
+
char *orig = "hello cera!";
char *s = malloc(strlen(orig) + 1);
+ cera_alloca_align(10, 30);
defer(free, s);
+ msg = catch(main2, orig);
+ if (msg)
+ {
+ printf("%s\n", msg);
+ return (-1);
+ }
char *s2 = malloc(strlen(orig) + 1);
defer(free, s2);
}