*.a
.norm
a.out
-a.out
+vgcore.*
+.direnv
+.norm_fail
NAME=ccera.a
-SRCS=src/arc.c src/defer2.c src/defer.c src/eval.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/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
-HEADERS=src/ccera.h
+HEADERS=src/ccera.h src/defer.h src/jmp.h src/panic.h
BUILDDIR=.build
OBJS=${SRCS:src/%.c=${BUILDDIR}/%.o}
-# no malloc leak detection as our union shenanigans trigger false positives
-CFLAGS=-Wall -Wextra -Werror -g -pthread -flto -ffat-lto-objects -fanalyzer -Wno-analyzer-malloc-leak
-CFLAGS += -O3
+CFLAGS=-Wall -Wextra -Werror -pthread -std=gnu23
+CFLAGS += -g
+#CFLAGS += -O3
+#CFLAGS += -flto -ffat-lto-objects
-CC=gcc
+CC=clang
AR=ar
watch -c -n 1 make
a.out: test.c ${NAME} ${THIS}
- ${CC} ${CFLAGS} ccera.a test.c
+ ${CC} ${CFLAGS} test.c ccera.a
test: a.out
valgrind ./a.out
--- /dev/null
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1778274207,
+ "narHash": "sha256-I4puXmX1iovcCHZlRmztO3vW0mAbbRvq4F8wgIMQ1MM=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "b3da656039dc7a6240f27b2ef8cc6a3ef3bccae7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
--- /dev/null
+{
+ description = "Cera in C!";
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ };
+ outputs =
+ {
+ nixpkgs,
+ ...
+ }:
+ let
+ system = "x86_64-linux";
+ pkgs = import nixpkgs { inherit system; };
+ in
+ {
+ devShells."${system}".default = pkgs.mkShell {
+ packages = with pkgs; [
+ clang
+ ];
+ };
+ };
+}
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/04/29 16:08:57 by agilliar #+# #+# */
-/* Updated: 2026/05/06 18:37:26 by agilliar ### ########.fr */
+/* Updated: 2026/05/10 22:11:53 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
# include <stdint.h>
# include <stdbool.h>
+# include <stddef.h>
+
+# include "jmp.h"
+# include "defer.h"
+# include "panic.h"
# if UINTPTR_MAX != 0xFFFFFFFFFFFFFFFF
# error "Platform's pointers are not 64 bits"
typedef uintptr_t t_usize;
typedef intptr_t t_isize;
-struct s_defer_pair
-{
- void (*f)(void *);
- void *dat;
- bool err;
-};
-
-# define MAX_DEFERS 8
-
-typedef struct s_defer
-{
- struct s_defer *prev;
- struct s_defer *next;
- t_usize len;
- struct s_defer_pair defers[MAX_DEFERS];
-} t_defer;
-
-__attribute__((__always_inline__))
-inline void *foo(void)
-{
- return (__builtin_alloca(10));
-}
-
-void defer_new(t_defer *store);
-void defer_delete(t_defer *store);
-void defer_re(t_defer *store);
-
-void defer(void *f, void *dat);
-void errdefer(void *f, void *dat);
-void defer_exit(t_defer *until);
-
-typedef struct s_panic_info
-{
- void *data;
- intptr_t jmp[5];
- t_defer *defer;
-} t_panic_info;
-
-void *catch(void (f)(void *), void *dat);
-
-void panic(void *err)
- __attribute__((noreturn, nonnull(1)));
-bool panicking(void);
-
typedef union u_value t_value;
typedef t_value (*t_builtin)(t_value);
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/05 17:31:36 by agilliar #+# #+# */
-/* Updated: 2026/05/06 18:37:20 by agilliar ### ########.fr */
+/* Updated: 2026/05/11 04:13:54 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
-#include "ccera.h"
-#include <stddef.h>
+#include "defer.h"
+#include <assert.h>
-t_defer **defer_store(void);
+static_assert((sizeof(t_defer_frame)) == 32);
+#define T_DEFER_FRAME_SIZE "32"
-void defer(void *f, void *dat)
-{
- t_defer *store;
+static_assert((alignof(t_defer_frame)) == 8);
+#define T_DEFER_FRAME_ALIGN "8"
- store = *defer_store();
- if (store->len == MAX_DEFERS)
- {
- ((void (*)(void *))f)(dat);
- panic("Maximum defers exeeded in scope");
- }
- store->defers[store->len].f = f;
- store->defers[store->len].dat = dat;
- store->defers[store->len].err = false;
- store->len++;
-}
+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"
+ );
-void errdefer(void *f, void *dat)
-{
- t_defer *store;
+// TODO: impl
+//
+//typedef struct s_defer_frame
+//{
+// void *ret_ptr;
+// void *stack_ptr;
+// struct s_defer_frame *prev;
+// t_defer *frame;
+//} t_defer_frame;
- store = *defer_store();
- if (store->len == MAX_DEFERS)
- {
- ((void (*)(void *))f)(dat);
- panic("Maximum defers exeeded in scope");
- }
- store->defers[store->len].f = f;
- store->defers[store->len].dat = dat;
- store->defers[store->len].err = true;
- store->len++;
+__attribute__((naked))
+void defer_unpatch_exit(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)));
}
-void defer_exit(t_defer *until)
+// TODO: impl
+//typedef struct s_defer
+//{
+// struct s_defer *prev;
+// void (*f)(void *);
+// void *dat;
+// bool err;
+//} t_defer;
+
+__attribute__((used))
+void defer_pop_frame(bool err)
{
- t_usize i;
- t_defer *store;
- struct s_defer_pair curr;
+ t_defer *curr;
- store = *defer_store();
- while (store)
+ curr = defer_store()->frame;
+ while (curr)
{
- i = 0;
- while (i < store->len)
- {
- curr = store->defers[i++];
- if (!panicking() && curr.err)
- continue ;
- curr.f(curr.dat);
- }
- if (store == until)
- break ;
- store = store->prev;
+ if (err || (!curr->err))
+ curr->f(curr->dat);
+ curr = curr->prev;
}
- *defer_store() = store;
+ if (defer_store()->prev)
+ *defer_store() = *defer_store()->prev;
}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* defer.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* 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 */
+/* */
+/* ************************************************************************** */
+
+#ifndef DEFER_H
+# define DEFER_H
+
+# include <stddef.h>
+
+typedef struct s_defer
+{
+ struct s_defer *prev;
+ void (*f)(void *);
+ void *dat;
+ bool err;
+} t_defer;
+
+typedef struct s_defer_frame
+{
+ void *ret_ptr;
+ void *stack_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)
+ __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;
+
+ ret_ptr = __builtin_return_address(0);
+ if (&defer_unpatch_exit != ret_ptr)
+ {
+ asm ("movq %0, 8(%%rbp)\n" : : "r" (&defer_unpatch_exit));
+ prev = __builtin_alloca(sizeof(t_defer_frame));
+ curr = defer_store();
+ *prev = *curr;
+ curr->ret_ptr = ret_ptr;
+ curr->frame = NULL;
+ }
+ defer_stack_update();
+ ret_ptr = __builtin_return_address(0);
+ __builtin_assume(&defer_unpatch_exit == ret_ptr);
+}
+
+__attribute__((always_inline))
+static inline void defer(void *f, void *dat)
+{
+ t_defer *curr;
+
+ curr = __builtin_alloca(sizeof(t_defer));
+ defer_patch();
+ curr->prev = defer_store()->frame;
+ curr->f = f;
+ curr->dat = dat;
+ curr->err = false;
+ defer_store()->frame = curr;
+}
+
+__attribute__((always_inline))
+static inline void errdefer(void *f, void *dat)
+{
+ t_defer *curr;
+
+ curr = __builtin_alloca(sizeof(t_defer));
+ defer_patch();
+ curr->prev = defer_store()->frame;
+ curr->f = f;
+ curr->dat = dat;
+ curr->err = true;
+ defer_store()->frame = curr;
+}
+
+#endif
+++ /dev/null
-/* ************************************************************************** */
-/* */
-/* ::: :::::::: */
-/* defer2.c :+: :+: :+: */
-/* +:+ +:+ +:+ */
-/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
-/* +#+#+#+#+#+ +#+ */
-/* Created: 2026/05/06 02:23:38 by agilliar #+# #+# */
-/* Updated: 2026/05/06 02:32:07 by agilliar ### ########.fr */
-/* */
-/* ************************************************************************** */
-
-#include "ccera.h"
-#include <stddef.h>
-
-t_defer **defer_store(void)
-{
- static __thread t_defer *store = NULL;
-
- return (&store);
-}
-
-void defer_new(t_defer *store)
-{
- store->len = 0;
- store->prev = *defer_store();
- if (store->prev)
- store->prev->next = store;
- store->next = NULL;
- *defer_store() = store;
-}
-
-void defer_delete(t_defer *store)
-{
- if (store == *defer_store())
- *defer_store() = store->prev;
- if (store->next)
- store->next->prev = store->prev;
- if (store->prev)
- store->prev->next = store->next;
-}
-
-void defer_re(t_defer *store)
-{
- defer_delete(store);
- defer_new(store);
-}
--- /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)));
+}
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* jmp.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* 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 */
+/* */
+/* ************************************************************************** */
+
+#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;
+
+bool cera_setjmp(void *buf[2])
+ __attribute__((returns_twice, naked));
+
+void cera_longjmp(void *buf[2])
+ __attribute__((noreturn, naked));
+
+#endif
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/05 15:38:33 by agilliar #+# #+# */
-/* Updated: 2026/05/05 21:25:00 by agilliar ### ########.fr */
+/* Updated: 2026/05/11 02:26:13 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include "ccera.h"
-#include <string.h>
-#include <stddef.h>
-#include <stdint.h>
-static t_panic_info *panic_store(void)
+/*
+t_panic_info *panic_store(void)
{
static __thread t_panic_info store;
return (&store);
}
-void *catch(void (f)(void *), void *dat)
+void *catch(void *f, void *dat)
{
void *res;
t_defer defer_store;
*panic_store() = prev;
return (res);
}
- f(dat);
+ ((void (*)(void *))f)(dat);
*panic_store() = prev;
return (NULL);
}
{
return (panic_store()->data != NULL);
}
+*/
--- /dev/null
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* panic.h :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* 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 */
+/* */
+/* ************************************************************************** */
+
+#ifndef PANIC_H
+# define PANIC_H
+
+typedef struct s_panic_info
+{
+ void *data;
+ void *jmp[2];
+ t_defer *defer;
+} t_panic_info;
+
+void *catch(void *f, void *dat);
+void panic(void *err)
+ __attribute__((noreturn, nonnull(1)));
+bool panicking(void);
+
+#endif
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/04/30 17:27:40 by agilliar #+# #+# */
-/* Updated: 2026/05/05 21:32:17 by agilliar ### ########.fr */
+/* Updated: 2026/05/06 19:28:43 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include "ccera.h"
-#include <stddef.h>
struct s_value_builtin *value_builtin_get(t_value value)
{
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/05/04 02:05:48 by agilliar #+# #+# */
-/* Updated: 2026/05/05 20:37:17 by agilliar ### ########.fr */
+/* Updated: 2026/05/06 19:29:20 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include "ccera.h"
-#include <stddef.h>
#include <string.h>
t_value value_copy(t_value value)
/* By: agilliar <agilliar@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/04/30 17:19:58 by agilliar #+# #+# */
-/* Updated: 2026/05/06 17:58:12 by agilliar ### ########.fr */
+/* Updated: 2026/05/11 04:12:03 by agilliar ### ########.fr */
/* */
/* ************************************************************************** */
#include "src/ccera.h"
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
+/*
void main2(void *s)
{
t_defer defer_store __attribute__((cleanup(defer_exit)));
int main(void)
{
- char *b = foo();
- b[0] = 'a';
- b[1] = '\0';
- printf("%s\n", b);
- if (char *err = catch(main2, "Hello cera!"))
+ char *err = catch(main2, "hello cera");
+ if (err)
{
printf("%s\n", err);
return (1);
}
}
+*/
+
+int main(void)
+{
+ char *orig = "hello cera!";
+ char *s = malloc(strlen(orig) + 1);
+ defer(free, s);
+ char *s2 = malloc(strlen(orig) + 1);
+ defer(free, s2);
+}