kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Minclude/cfree.h | 3+++
Minclude/cfree/cg.h | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/api/cg.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/api/lifecycle.c | 5+++++
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 &ap; 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; +}