commit 005f891059421c2be387dc3f64435f9e797eff2a
parent 651ae85e2d4b2a378ed9567ec16f77c29761e5fb
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 13 May 2026 10:04:08 -0700
Redesign public CG API
Diffstat:
| M | include/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 ≈ 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