commit 001f39c2a663a220893629d9ee90590e70504874
parent 13cc44e82229698acad65407f1cfb9b525ac6ebd
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 12 May 2026 19:00:45 -0700
cg api: add unop, va_list, extended intrinsics, structured scope results, push_int uint64_t, remove is_union
Diffstat:
4 files changed, 230 insertions(+), 22 deletions(-)
diff --git a/include/cfree.h b/include/cfree.h
@@ -425,6 +425,9 @@ const char* cfree_compiler_file_name(CfreeCompiler*, uint32_t file_id);
* entry never returns 0 for a non-NULL string. */
CfreeSym cfree_sym_intern(CfreeCompiler*, const char* str);
+/* Returns the diagnostic sink registered at compiler construction. */
+CfreeDiagSink* cfree_compiler_diag_sink(CfreeCompiler*);
+
/* ============================================================
* Writer dispatch (inline)
* ============================================================
diff --git a/include/cfree/cg.h b/include/cfree/cg.h
@@ -78,9 +78,9 @@ CfreeCgTypeId cfree_cg_type_qualified(CfreeCompiler*, CfreeCgTypeId base,
uint32_t quals);
CfreeCgTypeId cfree_cg_type_alias(CfreeCompiler*, CfreeSym name,
CfreeCgTypeId base);
-CfreeCgTypeId cfree_cg_type_record(CfreeCompiler*, CfreeSym tag, int is_union,
- const CfreeCgField* fields,
- uint32_t nfields);
+CfreeCgTypeId cfree_cg_type_record(CfreeCompiler*, CfreeSym tag,
+ const CfreeCgField* fields,
+ uint32_t nfields);
CfreeCgTypeId cfree_cg_type_enum(CfreeCompiler*, CfreeSym tag,
CfreeCgTypeId base,
const CfreeCgEnumValue* values,
@@ -89,6 +89,25 @@ CfreeCgTypeId cfree_cg_type_func(CfreeCompiler*, CfreeCgTypeId ret,
const CfreeCgTypeId* params, uint32_t nparams,
int variadic);
+/* Type shape queries */
+int cfree_cg_type_is_ptr(CfreeCompiler*, CfreeCgTypeId);
+CfreeCgTypeId cfree_cg_type_ptr_pointee(CfreeCompiler*, CfreeCgTypeId);
+
+int cfree_cg_type_is_func(CfreeCompiler*, CfreeCgTypeId);
+CfreeCgTypeId cfree_cg_type_func_ret(CfreeCompiler*, CfreeCgTypeId);
+uint32_t cfree_cg_type_func_nparams(CfreeCompiler*, CfreeCgTypeId);
+CfreeCgTypeId cfree_cg_type_func_param(CfreeCompiler*, CfreeCgTypeId,
+ uint32_t index);
+
+int cfree_cg_type_is_record(CfreeCompiler*, CfreeCgTypeId);
+uint32_t cfree_cg_type_record_nfields(CfreeCompiler*, CfreeCgTypeId);
+int cfree_cg_type_record_field(CfreeCompiler*, CfreeCgTypeId, uint32_t index,
+ CfreeCgField* out);
+
+/* Type layout queries */
+uint64_t cfree_cg_type_size(CfreeCompiler*, CfreeCgTypeId);
+uint32_t cfree_cg_type_align(CfreeCompiler*, CfreeCgTypeId);
+
typedef enum CfreeCgVisibility {
CFREE_CG_VIS_DEFAULT,
CFREE_CG_VIS_HIDDEN,
@@ -148,10 +167,24 @@ void cfree_cg_func_begin(CfreeCg*, CfreeSym name, CfreeCgTypeId fn_type,
CfreeCgDeclAttrs attrs);
void cfree_cg_func_end(CfreeCg*);
-/* Scope debug metadata is attached to the same nesting object used for
- * structured control flow; pass CFREE_CG_TYPE_NONE for statement-only scopes.
- * Break/continue are valid only for scopes the frontend treats as loop/block
- * control-flow targets. */
+/* Structured control flow scopes.
+ *
+ * result_type determines whether the scope carries a value:
+ * CFREE_CG_TYPE_NONE — statement scope, no result.
+ * otherwise — expression scope; break/continue carry a result.
+ *
+ * Stack effects when result_type != NONE:
+ * break(scope) → pop result, exit scope
+ * break_true(scope) → stack is [result, i1]; pop i1; if true, pop result
+ * and exit; otherwise pop result (leaving stack clean)
+ * break_false(scope) → same but exit on false
+ * scope_end(scope) → TOS is the fallthrough result
+ *
+ * When result_type == NONE, break_true/break_false pop only the i1 and
+ * branch; break is an unconditional exit with no value.
+ *
+ * continue/continue_true/continue_false never carry a result — they jump
+ * to the loop header, not the scope exit. */
CfreeCgScope cfree_cg_scope_begin(CfreeCg*, CfreeCgTypeId result_type);
void cfree_cg_scope_end(CfreeCg*, CfreeCgScope);
void cfree_cg_break(CfreeCg*, CfreeCgScope);
@@ -169,22 +202,33 @@ CfreeCgSlot cfree_cg_param_slot(CfreeCg*, uint32_t index, CfreeCgTypeId type,
* `align` 0 means target default stack alignment. */
void cfree_cg_alloca(CfreeCg*, CfreeCgTypeId result_ptr_type, uint32_t align);
-void cfree_cg_push_int(CfreeCg*, int64_t value, CfreeCgTypeId type);
+/* Variadic argument access. The frontend pushes the address of a va_list
+ * local (via push_local) before each call. The impl reads/writes the va_list
+ * state in memory per the target ABI. */
+void cfree_cg_va_start(CfreeCg*); /* pop &ap */
+void cfree_cg_va_arg(CfreeCg*, CfreeCgTypeId type); /* pop ≈ push value */
+void cfree_cg_va_end(CfreeCg*); /* pop &ap */
+void cfree_cg_va_copy(CfreeCg*); /* pop &dst, &src */
+
+void cfree_cg_push_int(CfreeCg*, uint64_t value, CfreeCgTypeId type);
void cfree_cg_push_float(CfreeCg*, double value, CfreeCgTypeId type);
-/* Anonymous immutable bytes in rodata; pushes a pointer to the first byte. */
-void cfree_cg_push_bytes(CfreeCg*, const uint8_t* str, size_t len);
+/* Anonymous immutable bytes in rodata; pushes a pointer to the first byte.
+ * pointee_type describes the logical value at the pushed address, enabling
+ * push_bytes + load to materialize arbitrary-width constants. */
+void cfree_cg_push_bytes(CfreeCg*, const uint8_t* str, size_t len,
+ CfreeCgTypeId pointee_type);
void cfree_cg_push_local(CfreeCg*, CfreeCgSlot slot);
void cfree_cg_push_symbol(CfreeCg*, CfreeSym name, CfreeCgTypeId type,
CfreeCgSymbolRefKind kind, int64_t addend);
void cfree_cg_load(CfreeCg*);
void cfree_cg_addr(CfreeCg*);
-void cfree_cg_store(CfreeCg*);
+void cfree_cg_store(CfreeCg*); /* [..., lv, rv] → [rv] */
void cfree_cg_dup(CfreeCg*);
void cfree_cg_swap(CfreeCg*);
void cfree_cg_drop(CfreeCg*);
-void cfree_cg_rot3(CfreeCg*);
+void cfree_cg_rot3(CfreeCg*); /* [..., a, b, c] → [..., b, c, a] */
typedef enum CfreeCgBinOp {
CFREE_CG_ADD,
@@ -215,7 +259,14 @@ typedef enum CfreeCgCmpOp {
CFREE_CG_GE_U,
} CfreeCgCmpOp;
+typedef enum CfreeCgUnOp {
+ CFREE_CG_NEG,
+ CFREE_CG_NOT,
+ CFREE_CG_BNOT,
+} CfreeCgUnOp;
+
void cfree_cg_binop(CfreeCg*, CfreeCgBinOp);
+void cfree_cg_unop(CfreeCg*, CfreeCgUnOp);
void cfree_cg_cmp(CfreeCg*, CfreeCgCmpOp);
void cfree_cg_convert(CfreeCg*, CfreeCgTypeId dst);
@@ -228,10 +279,19 @@ typedef enum CfreeCgIntrinsic {
CFREE_CG_INTRIN_BSWAP,
CFREE_CG_INTRIN_FRAME_ADDRESS,
CFREE_CG_INTRIN_RETURN_ADDRESS,
+ CFREE_CG_INTRIN_SETJMP, /* pop &buf; push i32 */
+ CFREE_CG_INTRIN_LONGJMP, /* pop &buf, val; no return */
+ CFREE_CG_INTRIN_ADD_OVERFLOW, /* pop a, b; push (result, ok_i1) */
+ CFREE_CG_INTRIN_SUB_OVERFLOW, /* pop a, b; push (result, ok_i1) */
+ CFREE_CG_INTRIN_MUL_OVERFLOW, /* pop a, b; push (result, ok_i1) */
+ CFREE_CG_INTRIN_PREFETCH, /* pop addr; no result */
+ CFREE_CG_INTRIN_EXPECT, /* pop val, expected; push val */
+ CFREE_CG_INTRIN_ASSUME_ALIGNED, /* pop ptr; push aligned ptr */
} CfreeCgIntrinsic;
-/* Pops nargs operands and pushes result_type unless result_type is
- * CFREE_CG_TYPE_NONE or void. */
+/* Pops nargs operands. Pushes result_type unless result_type is
+ * CFREE_CG_TYPE_NONE or void. Overflow intrinsics push two values:
+ * (result, ok_i1) regardless of result_type. */
void cfree_cg_intrinsic(CfreeCg*, CfreeCgIntrinsic, uint32_t nargs,
CfreeCgTypeId result_type);
@@ -326,4 +386,70 @@ void cfree_cg_data_symbol(CfreeCg*, CfreeCgSymbolRefKind kind, CfreeSym target,
int64_t addend, uint32_t nbytes);
void cfree_cg_data_end(CfreeCg*);
+/* ----- static inline composites ----- */
+
+/* Increment/decrement an lvalue in place. Stack: [lv] → [result].
+ * post=1 pushes the old value; post=0 pushes the new value.
+ * op is CFREE_CG_ADD or CFREE_CG_SUB. ty is the promoted integer type
+ * of the lvalue (used for the literal 1 step).
+ * Requires: cfree_cg_store pushes the stored value. */
+static inline void cfree_cg_inc_dec(CfreeCg* cg, CfreeCgBinOp op, int post,
+ CfreeCgTypeId ty) {
+ cfree_cg_dup(cg); /* [lv, lv] */
+ cfree_cg_load(cg); /* [lv, old] */
+ if (post) {
+ cfree_cg_dup(cg); /* [lv, old, old] */
+ cfree_cg_push_int(cg, 1, ty); /* [lv, old, old, 1] */
+ cfree_cg_binop(cg, op); /* [lv, old, new] */
+ cfree_cg_rot3(cg); /* [old, new, lv] */
+ cfree_cg_swap(cg); /* [old, lv, new] */
+ cfree_cg_store(cg); /* [old, new] */
+ cfree_cg_drop(cg); /* [old] */
+ } else {
+ cfree_cg_push_int(cg, 1, ty); /* [lv, old, 1] */
+ cfree_cg_binop(cg, op); /* [lv, new] */
+ cfree_cg_store(cg); /* [new] */
+ }
+}
+
+/* Extract bits [lo, lo+width) from TOS as an unsigned value.
+ * Stack: [value] → [field]. ty is the integer type of the value.
+ * width < 64 masks with (1<<width)-1; width == 64 keeps all bits. */
+static inline void cfree_cg_bitget(CfreeCg* cg, CfreeCgTypeId ty,
+ uint32_t lo, uint32_t width) {
+ if (lo > 0) {
+ cfree_cg_push_int(cg, lo, ty);
+ cfree_cg_binop(cg, CFREE_CG_SHR_U);
+ }
+ if (width < 64) {
+ cfree_cg_push_int(cg, (1ULL << width) - 1, ty);
+ cfree_cg_binop(cg, CFREE_CG_AND);
+ }
+}
+
+/* Insert the low width bits of src into dst at bit position lo.
+ * Stack: [dst, src] → [result]. ty is the integer type.
+ * result = (dst & ~field_mask) | ((src << lo) & field_mask)
+ * where field_mask = ((1<<width)-1) << lo. width < 64; for
+ * width == 64 use bitget + shift + or directly. */
+static inline void cfree_cg_bitset(CfreeCg* cg, CfreeCgTypeId ty,
+ uint32_t lo, uint32_t width) {
+ uint64_t field_mask = ((1ULL << width) - 1) << lo;
+ uint64_t clear_mask = ~field_mask;
+ cfree_cg_swap(cg); /* [src, dst] */
+ cfree_cg_dup(cg); /* [src, dst, dst] */
+ cfree_cg_push_int(cg, clear_mask, ty);
+ cfree_cg_binop(cg, CFREE_CG_AND); /* [src, dst, dst_cleared] */
+ cfree_cg_rot3(cg); /* [dst, dst_cleared, src] */
+ if (lo > 0) {
+ cfree_cg_push_int(cg, lo, ty);
+ cfree_cg_binop(cg, CFREE_CG_SHL); /* [dst, dst_cleared, src<<lo] */
+ }
+ cfree_cg_push_int(cg, field_mask, ty);
+ cfree_cg_binop(cg, CFREE_CG_AND); /* [dst, dst_cleared, bits] */
+ cfree_cg_rot3(cg); /* [dst_cleared, bits, dst] */
+ cfree_cg_drop(cg); /* [dst_cleared, bits] */
+ cfree_cg_binop(cg, CFREE_CG_OR); /* [result] */
+}
+
#endif
diff --git a/src/api/cg.c b/src/api/cg.c
@@ -3,6 +3,7 @@
#include <string.h>
#include "api/cg_api.h"
+#include "abi/abi.h"
#include "core/arena.h"
#include "core/heap.h"
#include "core/segvec.h"
@@ -153,6 +154,19 @@ static const Type* resolve_type(Compiler* c, CfreeCgTypeId id) {
return e ? e->type : NULL;
}
+static CgApiType* api_type_from_id(Compiler* c, CfreeCgTypeId id) {
+ u32 index;
+ CgApiState* s;
+ CgApiType* e;
+ if (!c || id == CFREE_CG_TYPE_NONE) return NULL;
+ if ((id >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) return NULL;
+ if (!decode_user_id(id, &index)) return NULL;
+ s = (CgApiState*)c->cg_api;
+ if (!s) return NULL;
+ e = CgApiTypes_at(&s->types, index);
+ return e;
+}
+
static u16 quals_to_internal(u32 quals) {
u16 q = 0;
if (quals & CFREE_CG_TQ_CONST) q |= Q_CONST;
@@ -252,14 +266,13 @@ CfreeCgTypeId cfree_cg_type_alias(CfreeCompiler* c, CfreeSym name,
return id;
}
-CfreeCgTypeId cfree_cg_type_record(CfreeCompiler* c, CfreeSym tag, int is_union,
- const CfreeCgField* fields,
- uint32_t nfields) {
+CfreeCgTypeId cfree_cg_type_record(CfreeCompiler* c, CfreeSym tag,
+ const CfreeCgField* fields,
+ uint32_t nfields) {
CfreeCgTypeId id;
CgApiType* e;
TypeRecordBuilder* b;
CfreeCgField* copied = NULL;
- TagDeclKind tag_kind = is_union ? TAG_UNION : TAG_STRUCT;
TagId tag_id;
if (!c || (nfields && !fields) || nfields > UINT16_MAX) {
return CFREE_CG_TYPE_NONE;
@@ -268,9 +281,9 @@ CfreeCgTypeId cfree_cg_type_record(CfreeCompiler* c, CfreeSym tag, int is_union,
copied = arena_array(&c->global->arena, CfreeCgField, nfields);
if (!copied) return CFREE_CG_TYPE_NONE;
}
- tag_id = type_tag_new(c->global, tag_kind, tag, (SrcLoc){0, 0, 0});
- b = type_record_begin(c->global, is_union ? TY_UNION : TY_STRUCT, tag_id,
- tag);
+
+ tag_id = type_tag_new(c->global, TAG_STRUCT, tag, (SrcLoc){0, 0, 0});
+ b = type_record_begin(c->global, TY_STRUCT, tag_id, tag);
if (!tag_id || !b) return CFREE_CG_TYPE_NONE;
for (u32 i = 0; i < nfields; ++i) {
const Type* fty = resolve_type(c, fields[i].type);
@@ -295,7 +308,7 @@ CfreeCgTypeId cfree_cg_type_record(CfreeCompiler* c, CfreeSym tag, int is_union,
e->count = nfields;
e->fields = copied;
e->kind = CG_API_TYPE_RECORD;
- e->is_union = is_union ? 1u : 0u;
+ e->is_union = 0;
return e->type ? id : CFREE_CG_TYPE_NONE;
}
@@ -379,6 +392,67 @@ const Type* cg_api_type_resolve(Compiler* c, CfreeCgTypeId id) {
return resolve_type(c, id);
}
+uint64_t cfree_cg_type_size(CfreeCompiler* c, CfreeCgTypeId id) {
+ const Type* ty = resolve_type(c, id);
+ return ty ? (uint64_t)abi_sizeof(c->abi, ty) : 0;
+}
+
+uint32_t cfree_cg_type_align(CfreeCompiler* c, CfreeCgTypeId id) {
+ const Type* ty = resolve_type(c, id);
+ return ty ? (uint32_t)abi_alignof(c->abi, ty) : 0;
+}
+
+int cfree_cg_type_is_ptr(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return e && e->kind == CG_API_TYPE_PTR;
+}
+
+CfreeCgTypeId cfree_cg_type_ptr_pointee(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return (e && e->kind == CG_API_TYPE_PTR) ? e->base : CFREE_CG_TYPE_NONE;
+}
+
+int cfree_cg_type_is_func(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return e && e->kind == CG_API_TYPE_FUNC;
+}
+
+CfreeCgTypeId cfree_cg_type_func_ret(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return (e && e->kind == CG_API_TYPE_FUNC) ? e->base : CFREE_CG_TYPE_NONE;
+}
+
+uint32_t cfree_cg_type_func_nparams(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return (e && e->kind == CG_API_TYPE_FUNC) ? e->count : 0;
+}
+
+CfreeCgTypeId cfree_cg_type_func_param(CfreeCompiler* c, CfreeCgTypeId id,
+ uint32_t index) {
+ CgApiType* e = api_type_from_id(c, id);
+ if (!e || e->kind != CG_API_TYPE_FUNC || index >= e->count)
+ return CFREE_CG_TYPE_NONE;
+ return e->params[index];
+}
+
+int cfree_cg_type_is_record(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return e && e->kind == CG_API_TYPE_RECORD;
+}
+
+uint32_t cfree_cg_type_record_nfields(CfreeCompiler* c, CfreeCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ return (e && e->kind == CG_API_TYPE_RECORD) ? e->count : 0;
+}
+
+int cfree_cg_type_record_field(CfreeCompiler* c, CfreeCgTypeId id,
+ uint32_t index, CfreeCgField* out) {
+ CgApiType* e = api_type_from_id(c, id);
+ if (!e || e->kind != CG_API_TYPE_RECORD || !out || index >= e->count) return 1;
+ *out = e->fields[index];
+ return 0;
+}
+
void cg_api_fini(Compiler* c) {
CgApiState* s;
if (!c || !c->cg_api) return;
diff --git a/src/api/lifecycle.c b/src/api/lifecycle.c
@@ -47,3 +47,8 @@ int cfree_register_frontend(CfreeCompiler* c, CfreeLanguage lang,
c->frontends[lang] = fn;
return 0;
}
+
+CfreeDiagSink* cfree_compiler_diag_sink(CfreeCompiler* c) {
+ if (!c || !c->env) return NULL;
+ return c->env->diag;
+}