kit

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

commit 005f891059421c2be387dc3f64435f9e797eff2a
parent 651ae85e2d4b2a378ed9567ec16f77c29761e5fb
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 13 May 2026 10:04:08 -0700

Redesign public CG API

Diffstat:
Minclude/cfree/cg.h | 475++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 302 insertions(+), 173 deletions(-)

diff --git a/include/cfree/cg.h b/include/cfree/cg.h @@ -3,18 +3,28 @@ #include <cfree.h> +/* ============================================================ + * Handles + * ============================================================ */ + typedef struct CfreeCg CfreeCg; -typedef struct CfreeCgValue CfreeCgValue; typedef uint32_t CfreeCgLabel; typedef uint32_t CfreeCgScope; typedef uint32_t CfreeCgSlot; +typedef uint32_t CfreeCgSym; typedef uint32_t CfreeCgTypeId; #define CFREE_CG_LABEL_NONE 0u #define CFREE_CG_SCOPE_NONE 0u +#define CFREE_CG_SLOT_NONE 0u +#define CFREE_CG_SYM_NONE 0u #define CFREE_CG_TYPE_NONE 0u +/* ============================================================ + * Types + * ============================================================ */ + typedef enum CfreeCgBuiltinType { CFREE_CG_BUILTIN_VOID, CFREE_CG_BUILTIN_BOOL, @@ -30,26 +40,12 @@ typedef enum CfreeCgBuiltinType { CFREE_CG_BUILTIN_USIZE, CFREE_CG_BUILTIN_F32, CFREE_CG_BUILTIN_F64, - CFREE_CG_BUILTIN_VA_LIST, + CFREE_CG_BUILTIN_VARARG_STATE, CFREE_CG_BUILTIN_COUNT, } CfreeCgBuiltinType; typedef struct CfreeCgBuiltinTypes { - CfreeCgTypeId void_; - CfreeCgTypeId bool_; - CfreeCgTypeId i8; - CfreeCgTypeId u8; - CfreeCgTypeId i16; - CfreeCgTypeId u16; - CfreeCgTypeId i32; - CfreeCgTypeId u32; - CfreeCgTypeId i64; - CfreeCgTypeId u64; - CfreeCgTypeId isize; - CfreeCgTypeId usize; - CfreeCgTypeId f32; - CfreeCgTypeId f64; - CfreeCgTypeId va_list; + CfreeCgTypeId id[CFREE_CG_BUILTIN_COUNT]; } CfreeCgBuiltinTypes; typedef enum CfreeCgTypeQual { @@ -74,88 +70,140 @@ typedef struct CfreeCgEnumValue { * compiler; aliases, records, and enums allocate fresh user-facing * identities. */ CfreeCgBuiltinTypes cfree_cg_builtin_types(CfreeCompiler*); + +/* Interned structural types. */ +CfreeCgTypeId cfree_cg_type_func(CfreeCompiler*, CfreeCgTypeId ret, + const CfreeCgTypeId* params, uint32_t nparams, + int abi_variadic); CfreeCgTypeId cfree_cg_type_ptr(CfreeCompiler*, CfreeCgTypeId pointee); CfreeCgTypeId cfree_cg_type_array(CfreeCompiler*, CfreeCgTypeId elem, uint32_t count); CfreeCgTypeId cfree_cg_type_qualified(CfreeCompiler*, CfreeCgTypeId base, uint32_t quals); + +/* Fresh nominal/source-facing types. */ CfreeCgTypeId cfree_cg_type_alias(CfreeCompiler*, CfreeSym name, CfreeCgTypeId base); CfreeCgTypeId cfree_cg_type_record(CfreeCompiler*, CfreeSym tag, - const CfreeCgField* fields, - uint32_t nfields); + const CfreeCgField* fields, + uint32_t nfields); CfreeCgTypeId cfree_cg_type_enum(CfreeCompiler*, CfreeSym tag, CfreeCgTypeId base, const CfreeCgEnumValue* values, uint32_t nvalues); -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); +/* Type queries. */ +uint64_t cfree_cg_type_size(CfreeCompiler*, CfreeCgTypeId); +uint32_t cfree_cg_type_align(CfreeCompiler*, CfreeCgTypeId); +int cfree_cg_type_is_ptr(CfreeCompiler*, CfreeCgTypeId); int cfree_cg_type_is_func(CfreeCompiler*, CfreeCgTypeId); +int cfree_cg_type_is_record(CfreeCompiler*, CfreeCgTypeId); + +CfreeCgTypeId cfree_cg_type_ptr_pointee(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); + 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); + CfreeCgField* out); -/* Type layout queries */ -uint64_t cfree_cg_type_size(CfreeCompiler*, CfreeCgTypeId); -uint32_t cfree_cg_type_align(CfreeCompiler*, CfreeCgTypeId); +/* ============================================================ + * Declarations and Symbols + * ============================================================ */ typedef enum CfreeCgVisibility { + /* Externally visible, object-format-default visibility. On ELF this maps to + * STV_DEFAULT and remains preemptible when bind/output mode allow it. On + * formats without an equivalent visibility field, this is the normal visible + * symbol state. Local-bind symbols are still local. */ CFREE_CG_VIS_DEFAULT, CFREE_CG_VIS_HIDDEN, CFREE_CG_VIS_PROTECTED, } CfreeCgVisibility; -typedef enum CfreeCgDeclFlag { - CFREE_CG_DECL_NONE = 0, - CFREE_CG_DECL_DEFINED = 1u << 0, - CFREE_CG_DECL_READONLY = 1u << 1, - CFREE_CG_DECL_TLS = 1u << 2, - CFREE_CG_DECL_COMMON = 1u << 3, - CFREE_CG_DECL_USED = 1u << 4, - CFREE_CG_DECL_NORETURN = 1u << 5, -} CfreeCgDeclFlag; +typedef enum CfreeCgSymbolFlag { + CFREE_CG_SYMFLAG_NONE = 0, + CFREE_CG_SYM_USED = 1u << 0, + CFREE_CG_SYM_DLLIMPORT = 1u << 1, + CFREE_CG_SYM_DLLEXPORT = 1u << 2, +} CfreeCgSymbolFlag; + +typedef struct CfreeCgSymbolAttrs { + CfreeSymBind bind; + CfreeCgVisibility visibility; + uint32_t flags; /* CfreeCgSymbolFlag */ +} CfreeCgSymbolAttrs; + +typedef enum CfreeCgFuncFlag { + CFREE_CG_FUNC_NONE = 0, + CFREE_CG_FUNC_NORETURN = 1u << 0, + CFREE_CG_FUNC_IFUNC = 1u << 1, +} CfreeCgFuncFlag; + +typedef struct CfreeCgFuncAttrs { + uint32_t flags; /* CfreeCgFuncFlag */ +} CfreeCgFuncAttrs; typedef enum CfreeCgTlsModel { - CFREE_CG_TLS_DEFAULT, + /* Let the target/backend choose from the symbol properties, visibility, + * output mode, and object format. Non-AUTO values are frontend requests + * from source attributes or driver options; unsupported requests should be + * diagnosed or conservatively widened. Object-format mechanisms such as + * Mach-O TLVP are target-selected implementation details. */ + CFREE_CG_TLS_AUTO, CFREE_CG_TLS_LOCAL_EXEC, CFREE_CG_TLS_INITIAL_EXEC, CFREE_CG_TLS_LOCAL_DYNAMIC, CFREE_CG_TLS_GENERAL_DYNAMIC, - CFREE_CG_TLS_TLVP, } CfreeCgTlsModel; -typedef struct CfreeCgDeclAttrs { - CfreeSymBind bind; - CfreeCgVisibility visibility; +typedef enum CfreeCgObjectFlag { + CFREE_CG_OBJ_NONE = 0, + CFREE_CG_OBJ_READONLY = 1u << 0, + CFREE_CG_OBJ_TLS = 1u << 1, +} CfreeCgObjectFlag; + +typedef struct CfreeCgObjectAttrs { CfreeCgTlsModel tls_model; - CfreeSym section; /* 0 = default section */ - uint32_t align; /* 0 = natural */ - uint32_t flags; /* CfreeCgDeclFlag */ + uint32_t flags; /* CfreeCgObjectFlag */ +} CfreeCgObjectAttrs; + +typedef enum CfreeCgDeclKind { + CFREE_CG_DECL_FUNC, + CFREE_CG_DECL_OBJECT, +} CfreeCgDeclKind; + +typedef struct CfreeCgDeclAttrs { + CfreeCgDeclKind kind; + CfreeCgSymbolAttrs sym; + union { + CfreeCgFuncAttrs func; + CfreeCgObjectAttrs object; + } as; } CfreeCgDeclAttrs; -typedef enum CfreeCgSymbolRefKind { - CFREE_CG_SYMREF_ADDR, - CFREE_CG_SYMREF_PCREL, - CFREE_CG_SYMREF_GOT, - CFREE_CG_SYMREF_PLT, - CFREE_CG_SYMREF_TLS_LE, - CFREE_CG_SYMREF_TLS_IE, - CFREE_CG_SYMREF_TLS_LD, - CFREE_CG_SYMREF_TLS_GD, - CFREE_CG_SYMREF_TLVP, -} CfreeCgSymbolRefKind; +/* The declared type is the function type for function declarations and the + * object type for object declarations. A nonzero name is the linkage name + * before target object-format decoration; frontends should uniquify internal + * symbols whose source spelling is not link-unique. + * + * Undefined weak references are ordinary declarations with sym.bind = + * CFREE_SB_WEAK and no definition. Weak aliases are aliases whose attrs bind + * is CFREE_SB_WEAK. */ +CfreeCgSym cfree_cg_decl(CfreeCg*, CfreeSym name, CfreeCgTypeId type, + CfreeCgDeclAttrs attrs); + +/* Defines alias_name as another symbol for target. attrs supplies the alias + * symbol's binding, visibility, and platform export/import flags. */ +CfreeCgSym cfree_cg_alias(CfreeCg*, CfreeSym alias_name, CfreeCgSym target, + CfreeCgSymbolAttrs attrs); + +/* ============================================================ + * Lifecycle and Source Locations + * ============================================================ */ CfreeCg* cfree_cg_new(CfreeCompiler*, CfreeObjBuilder* out); void cfree_cg_free(CfreeCg*); @@ -164,30 +212,36 @@ void cfree_cg_free(CfreeCg*); * data-definition debug records use the current location. */ void cfree_cg_set_loc(CfreeCg*, CfreeSrcLoc); -void cfree_cg_func_decl(CfreeCg*, CfreeSym name, CfreeCgTypeId fn_type, - CfreeCgDeclAttrs attrs); -void cfree_cg_func_begin(CfreeCg*, CfreeSym name, CfreeCgTypeId fn_type, - CfreeCgDeclAttrs attrs); +/* ============================================================ + * Function Bodies and Locals + * ============================================================ */ + +void cfree_cg_func_begin(CfreeCg*, CfreeCgSym sym); void cfree_cg_func_end(CfreeCg*); +CfreeCgSlot cfree_cg_local_slot(CfreeCg*, CfreeCgTypeId type, CfreeSym name); +CfreeCgSlot cfree_cg_param_slot(CfreeCg*, uint32_t index, CfreeCgTypeId type, + CfreeSym name); + +/* ============================================================ + * Control flow + * ============================================================ */ + /* 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. + * CFREE_CG_TYPE_NONE - statement scope, no result. + * otherwise - expression scope; break carries 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 + * break(scope) -> pop result, exit scope + * break_true(scope) -> stack is [result, bool]; pop bool; if true, pop + * result and exit; otherwise pop result + * 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. */ + * 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); @@ -197,34 +251,52 @@ void cfree_cg_continue(CfreeCg*, CfreeCgScope); void cfree_cg_continue_true(CfreeCg*, CfreeCgScope); void cfree_cg_continue_false(CfreeCg*, CfreeCgScope); -CfreeCgSlot cfree_cg_local_slot(CfreeCg*, CfreeCgTypeId type, CfreeSym name); -CfreeCgSlot cfree_cg_param_slot(CfreeCg*, uint32_t index, CfreeCgTypeId type, - CfreeSym name); +/* Unstructured labels and jumps. */ +CfreeCgLabel cfree_cg_label_new(CfreeCg*); +void cfree_cg_label_place(CfreeCg*, CfreeCgLabel); +void cfree_cg_jump(CfreeCg*, CfreeCgLabel); +void cfree_cg_branch_true(CfreeCg*, CfreeCgLabel); +void cfree_cg_branch_false(CfreeCg*, CfreeCgLabel); -/* Dynamic stack allocation. Pops size in bytes and pushes result_ptr_type. - * `align` 0 means target default stack alignment. */ -void cfree_cg_alloca(CfreeCg*, CfreeCgTypeId result_ptr_type, uint32_t align); +/* ============================================================ + * Value Stack and Lvalues + * ============================================================ */ -/* 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_dup(CfreeCg*); +void cfree_cg_swap(CfreeCg*); +void cfree_cg_drop(CfreeCg*); +void cfree_cg_rot3(CfreeCg*); /* [..., a, b, c] -> [..., b, c, a] */ 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 rvalue to the first - * byte. pointee_type describes the logical value at that address, enabling - * push_bytes + indirect + 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); -/* Pushes a pointer/address rvalue for the symbol. `type` is the symbol's - * declared object/function type, not the resulting pointer type. */ -void cfree_cg_push_symbol(CfreeCg*, CfreeSym name, CfreeCgTypeId type, - CfreeCgSymbolRefKind kind, int64_t addend); + +/* Anonymous immutable data. Returns a local readonly object symbol; callers + * can materialize its address with push_symbol_addr. pointee_type describes + * the logical value at that address, enabling const_data + indirect + load to + * materialize arbitrary-width constants. */ +CfreeCgSym cfree_cg_const_data(CfreeCg*, const uint8_t* data, size_t len, + uint32_t align, CfreeCgTypeId pointee_type); + +/* Pushes &sym + addend as a pointer rvalue. For TLS objects this means the + * address of the current thread's instance; the target chooses LE/IE/GD/TLVP + * or equivalent lowering from the symbol attrs and output mode. */ +void cfree_cg_push_symbol_addr(CfreeCg*, CfreeCgSym sym, int64_t addend); + +/* Pushes an lvalue backed by sym + addend. For TLS objects this materializes + * the current thread's instance address as needed and then treats it as an + * indirect lvalue. */ +void cfree_cg_push_symbol_lvalue(CfreeCg*, CfreeCgSym sym, int64_t addend); + +/* Computes base + offset + index * elemsz and pushes the element lvalue. + * Stack is [base, index]. elemsz is inferred from the base pointer/array + * type; index may be a constant produced by cfree_cg_push_int. */ +void cfree_cg_index(CfreeCg*, uint32_t offset); + +/* Pops a record lvalue and pushes the field lvalue. Offset is inferred from + * the record type and field_index. Use cfree_cg_addr after this when an + * address is required. */ +void cfree_cg_field(CfreeCg*, uint32_t field_index); /* Converts a pointer rvalue TOS from *T to an lvalue T. */ void cfree_cg_indirect(CfreeCg*); @@ -232,10 +304,25 @@ void cfree_cg_load(CfreeCg*); void cfree_cg_addr(CfreeCg*); void cfree_cg_store(CfreeCg*); /* [..., lv, rv] -> [] */ -void cfree_cg_dup(CfreeCg*); -void cfree_cg_swap(CfreeCg*); -void cfree_cg_drop(CfreeCg*); -void cfree_cg_rot3(CfreeCg*); /* [..., a, b, c] → [..., b, c, a] */ +/* ============================================================ + * ABI variadic argument access + * ============================================================ */ + +/* This models only target calling-convention varargs; higher-level rest + * parameters should lower to explicit aggregate parameters before reaching CG. + * + * The frontend allocates a local of CFREE_CG_BUILTIN_VARARG_STATE and pushes + * that local's address before each operation. The implementation reads and + * writes the state in memory according to the target ABI. */ +void cfree_cg_vararg_start(CfreeCg*); /* pop &state */ +void cfree_cg_vararg_next(CfreeCg*, + CfreeCgTypeId type); /* pop &state; push value */ +void cfree_cg_vararg_end(CfreeCg*); /* pop &state */ +void cfree_cg_vararg_copy(CfreeCg*); /* pop &dst, &src */ + +/* ============================================================ + * Operators, Calls, Intrinsics, and Atomics + * ============================================================ */ typedef enum CfreeCgBinOp { CFREE_CG_ADD, @@ -277,6 +364,16 @@ void cfree_cg_unop(CfreeCg*, CfreeCgUnOp); void cfree_cg_cmp(CfreeCg*, CfreeCgCmpOp); void cfree_cg_convert(CfreeCg*, CfreeCgTypeId dst); +/* cfree_cg_call pops a computed function pointer plus nargs arguments. + * cfree_cg_call_symbol emits a direct call to the declared function symbol, + * allowing the backend/linker to choose PLT/stub/IAT/direct/IFUNC handling. */ +void cfree_cg_call(CfreeCg*, uint32_t nargs, CfreeCgTypeId fn_type); +void cfree_cg_tail_call(CfreeCg*, uint32_t nargs, CfreeCgTypeId fn_type); +void cfree_cg_call_symbol(CfreeCg*, CfreeCgSym sym, uint32_t nargs); +void cfree_cg_tail_call_symbol(CfreeCg*, CfreeCgSym sym, uint32_t nargs); +void cfree_cg_ret(CfreeCg*); +void cfree_cg_ret_void(CfreeCg*); + typedef enum CfreeCgIntrinsic { CFREE_CG_INTRIN_TRAP, CFREE_CG_INTRIN_UNREACHABLE, @@ -286,20 +383,31 @@ typedef enum CfreeCgIntrinsic { CFREE_CG_INTRIN_BSWAP, 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 */ + CFREE_CG_INTRIN_ADD_OVERFLOW, /* pop a, b; push result, ok_bool */ + CFREE_CG_INTRIN_SUB_OVERFLOW, /* pop a, b; push result, ok_bool */ + CFREE_CG_INTRIN_MUL_OVERFLOW, /* pop a, b; push result, ok_bool */ + 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 */ + CFREE_CG_INTRIN_MEMCPY, /* pop dst, src, n; push dst */ + CFREE_CG_INTRIN_MEMMOVE, /* pop dst, src, n; push dst */ + CFREE_CG_INTRIN_MEMSET, /* pop dst, byte, n; push dst */ + CFREE_CG_INTRIN_MEMCMP, /* pop lhs, rhs, n; push i32 */ } CfreeCgIntrinsic; /* 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. */ + * result, ok_bool regardless of result_type. */ void cfree_cg_intrinsic(CfreeCg*, CfreeCgIntrinsic, uint32_t nargs, CfreeCgTypeId result_type); +/* Fixed-size aggregate memory operations. Stack: + * memcpy/memmove: [dst, src] -> [] + * memset: [dst] -> [] */ +void cfree_cg_memcpy(CfreeCg*, uint32_t size, uint32_t align); +void cfree_cg_memmove(CfreeCg*, uint32_t size, uint32_t align); +void cfree_cg_memset(CfreeCg*, uint8_t val, uint32_t size, uint32_t align); + typedef enum CfreeCgAtomicOp { CFREE_CG_ATOMIC_XCHG, CFREE_CG_ATOMIC_ADD, @@ -322,11 +430,15 @@ typedef enum CfreeCgMemOrder { void cfree_cg_atomic_load(CfreeCg*, CfreeCgMemOrder); void cfree_cg_atomic_store(CfreeCg*, CfreeCgMemOrder); void cfree_cg_atomic_rmw(CfreeCg*, CfreeCgAtomicOp, CfreeCgMemOrder); -/* Stack: [ptr, expected, desired] -> [prior, ok_i1]. */ +/* Stack: [ptr, expected, desired] -> [prior, ok_bool]. */ void cfree_cg_atomic_cmpxchg(CfreeCg*, CfreeCgMemOrder success, CfreeCgMemOrder failure); void cfree_cg_atomic_fence(CfreeCg*, CfreeCgMemOrder); +/* ============================================================ + * Inline Assembly + * ============================================================ */ + typedef enum CfreeCgAsmDir { CFREE_CG_ASM_IN, CFREE_CG_ASM_OUT, @@ -357,66 +469,83 @@ void cfree_cg_inline_asm(CfreeCg*, CfreeSym tmpl, const CfreeSym* clobbers, uint32_t nclobbers, uint32_t flags); -CfreeCgLabel cfree_cg_label_new(CfreeCg*); -void cfree_cg_label_place(CfreeCg*, CfreeCgLabel); -void cfree_cg_jump(CfreeCg*, CfreeCgLabel); -void cfree_cg_branch_true(CfreeCg*, CfreeCgLabel); -void cfree_cg_branch_false(CfreeCg*, CfreeCgLabel); - -void cfree_cg_memcpy(CfreeCg*, uint32_t size, uint32_t align); -void cfree_cg_memset(CfreeCg*, uint8_t val, uint32_t size, uint32_t align); +/* ============================================================ + * Data Definitions + * ============================================================ */ -/* Computes base + offset + index * elemsz and pushes the element lvalue. - * Stack is [base, index]. elemsz is inferred from the base pointer/array - * type; index may be a constant produced by cfree_cg_push_int. */ -void cfree_cg_index(CfreeCg*, uint32_t offset); +typedef enum CfreeCgDataDefFlag { + CFREE_CG_DATADEF_NONE = 0, + CFREE_CG_DATADEF_RETAIN = 1u << 0, + CFREE_CG_DATADEF_MERGE = 1u << 1, + CFREE_CG_DATADEF_STRINGS = 1u << 2, +} CfreeCgDataDefFlag; -/* Pops a record lvalue and pushes the field lvalue. Offset is inferred from - * the record type and field_index. Use cfree_cg_addr after this when an - * address is required. */ -void cfree_cg_field(CfreeCg*, uint32_t field_index); - -void cfree_cg_call(CfreeCg*, uint32_t nargs, CfreeCgTypeId fn_type); -void cfree_cg_tail_call(CfreeCg*, uint32_t nargs, CfreeCgTypeId fn_type); -void cfree_cg_ret(CfreeCg*); -void cfree_cg_ret_void(CfreeCg*); +typedef struct CfreeCgDataDefAttrs { + CfreeSym section; /* 0 = target default for the symbol */ + uint32_t align; /* 0 = natural */ + uint32_t entsize; /* 0 = target default; used by merge/string data */ + uint32_t flags; /* CfreeCgDataDefFlag */ +} CfreeCgDataDefAttrs; + +/* data_begin defines storage for an already-declared object symbol. + * data_common defines tentative/common zero-initialized storage when the + * target format supports it. */ +void cfree_cg_data_begin(CfreeCg*, CfreeCgSym sym, CfreeCgDataDefAttrs attrs); +void cfree_cg_data_common(CfreeCg*, CfreeCgSym sym, uint64_t size, + uint32_t align); +void cfree_cg_data_end(CfreeCg*); -/* Global data definitions. Use data_symbol for address constants in data - * initializers; code references use push_symbol. */ -void cfree_cg_data_decl(CfreeCg*, CfreeSym name, CfreeCgTypeId type, - CfreeCgDeclAttrs attrs); -void cfree_cg_data_begin(CfreeCg*, CfreeSym name, CfreeCgTypeId type, - CfreeCgDeclAttrs attrs); +/* Appends to the currently open data definition. */ +void cfree_cg_data_align(CfreeCg*, uint32_t align); +void cfree_cg_data_pad(CfreeCg*, uint64_t size, uint8_t value); +void cfree_cg_data_int(CfreeCg*, uint64_t value, CfreeCgTypeId type); +void cfree_cg_data_float(CfreeCg*, double value, CfreeCgTypeId type); void cfree_cg_data_bytes(CfreeCg*, const uint8_t* data, size_t len); void cfree_cg_data_zero(CfreeCg*, uint64_t size); -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 ----- */ +/* Relocatable data expressions. These describe the value encoded in the data + * stream; they do not request a lowering strategy such as GOT, PLT, TLVP, or + * a TLS access model. width is the encoded field width in bytes. */ +void cfree_cg_data_addr(CfreeCg*, CfreeCgSym target, int64_t addend, + uint32_t width); +void cfree_cg_data_pcrel(CfreeCg*, CfreeCgSym target, int64_t addend, + uint32_t width); +void cfree_cg_data_symdiff(CfreeCg*, CfreeCgSym lhs, CfreeCgSym rhs, + int64_t addend, uint32_t width); + +/* ============================================================ + * Static Inline Convenience Operations + * ============================================================ */ + +static inline void cfree_cg_push_bytes(CfreeCg* cg, const uint8_t* data, + size_t len, + CfreeCgTypeId pointee_type) { + CfreeCgSym sym = cfree_cg_const_data(cg, data, len, 0, pointee_type); + cfree_cg_push_symbol_addr(cg, sym, 0); +} -/* Increment/decrement an lvalue in place. Stack: [lv] → [result]. +/* 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). */ + * of the lvalue. */ 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] */ + 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] */ + 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] */ } else { - cfree_cg_push_int(cg, 1, ty); /* [lv, old, 1] */ - cfree_cg_binop(cg, op); /* [lv, new] */ - cfree_cg_dup(cg); /* [lv, new, new] */ - cfree_cg_rot3(cg); /* [new, new, lv] */ - cfree_cg_swap(cg); /* [new, lv, new] */ - cfree_cg_store(cg); /* [new] */ + cfree_cg_push_int(cg, 1, ty); /* [lv, old, 1] */ + cfree_cg_binop(cg, op); /* [lv, new] */ + cfree_cg_dup(cg); /* [lv, new, new] */ + cfree_cg_rot3(cg); /* [new, new, lv] */ + cfree_cg_swap(cg); /* [new, lv, new] */ + cfree_cg_store(cg); /* [new] */ } } @@ -443,10 +572,10 @@ static inline void cfree_cg_if_end(CfreeCg* cg, CfreeCgIf it) { } /* Extract bits [lo, lo+width) from TOS as an unsigned value. - * Stack: [value] → [field]. ty is the integer type of the 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) { +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); @@ -458,28 +587,28 @@ static inline void cfree_cg_bitget(CfreeCg* cg, CfreeCgTypeId ty, } /* Insert the low width bits of src into dst at bit position lo. - * Stack: [dst, src] → [result]. ty is the integer type. + * 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) { + * 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_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] */ + 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_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] */ + 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