kit

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

commit 9ef2979b70072891cc096bffa3b00a6db7a3f68d
parent 4d79515eb873bad5a8cd75952628e0eebe1f2df1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 14 May 2026 07:28:34 -0700

Add CG type registry

Diffstat:
Adoc/cg-type-migration-plan.md | 404+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/api/cg.c | 627+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/api/cg_api.h | 8++++++++
Mtest/api/cg_type_test.c | 65++++++++++++++++++++++++++++++++++++++++++++++-------------------
4 files changed, 995 insertions(+), 109 deletions(-)

diff --git a/doc/cg-type-migration-plan.md b/doc/cg-type-migration-plan.md @@ -0,0 +1,404 @@ +# CG Type Migration Plan + +## Goal + +Make the C frontend just another language frontend, like `lang/toy`, while +removing the C language `Type` dependency from `src/`. + +The C frontend should keep its rich C semantic type system privately in +`lang/c`. Codegen, ABI, arch lowering, optimizer, and object emission should +use a narrower, language-neutral `CgType` model constructed only through the +public `include/cfree/cg.h` type constructors. + +## Status Checklist + +Keep this section current as migration work lands. + +- [x] Phase 1: Add an internal `CgType` payload behind `CfreeCgTypeId`. +- [x] Phase 1: Add internal `cg_type_*` lookup, layout, and classification + helpers. +- [x] Phase 1: Populate `CgType` from public CG constructors and the temporary + legacy C `Type*` import bridge. +- [x] Phase 1: Move public CG type query APIs to `CgType`. +- [ ] Phase 1: Move `CgType` into a dedicated internal header/module if the + next migration slice needs it outside `src/api/cg.c`. +- [ ] Phase 2: Cache lowered `CfreeCgTypeId` values in the C frontend or in a + frontend-owned `Type*` map. +- [ ] Phase 2: Replace recursive-record placeholder lowering with a real + forward/begin/complete public CG record API. +- [ ] Phase 3: Replace stored `const Type*` in `src/api/cg.c` state with + `CfreeCgTypeId` or `CgType` facts. +- [ ] Phase 4: Migrate ABI APIs and record/function caches from C `Type*` to + CG type handles. +- [ ] Phase 5: Migrate `CGTarget` and arch lowering away from C semantic + types. +- [ ] Phase 6: Migrate optimizer and generic debug bridges away from C + semantic types. +- [ ] Phase 7: Remove legacy C type bridges and shim headers. +- [ ] Phase 8: Register C through the frontend mechanism and remove direct + `src` dependencies on `lang/c`. + +## Target Boundary + +- `lang/c/Type`: C semantic type. It carries C-only facts such as qualifiers, + typedef/tag behavior, incomplete types, decay rules, bitfield syntax, and + source-level compatibility. +- `CgType`: language-neutral codegen type. It carries only storage, layout, + calling convention, and lowering information expressible through + `include/cfree/cg.h`. +- `src/`: must not include or depend on `lang/c` headers. +- `ObjBuilder`: should see symbols, sections, relocs, and bytes, not C types. +- `CGTarget`: should see codegen facts such as size, alignment, register class, + pointer/float/int shape, and operation flags, not C semantic facts. + +## Why This Is Needed + +Today `CfreeCgTypeId` is public, but internally `src/api/cg.c` resolves it back +to the C frontend `Type*`. That makes the public CG API a wrapper around the C +type system and keeps `src` coupled to `lang/c` through compatibility headers. + +The main current dependency shape is: + +- public `cfree_cg_type_*` constructors create/import C `Type*` +- CG stack values, operands, slots, symbols, ABI calls, and target descriptors + store `const Type*` +- ABI and arch helpers inspect `Type*` directly +- `src/type/type.h` is currently a shim to `lang/c/type/type.h` + +The migration inverts that relationship. C `Type` lowers to `CfreeCgTypeId`; +CG internals resolve `CfreeCgTypeId` to `CgType`. + +## CgType Shape + +Add an internal `CgType` representation, probably under `src/api/cg_type.h` or +`src/cg/type.h`. + +It should model only concepts from public `include/cfree/cg.h`: + +```c +typedef struct CgType CgType; + +typedef struct CgTypeField { + CfreeSym name; + CfreeCgTypeId type; + uint64_t offset; + uint32_t align_override; +} CgTypeField; + +struct CgType { + CfreeCgTypeKind kind; + uint64_t size; + uint32_t align; + + union { + struct { + uint32_t width; + } integer; + + struct { + uint32_t width; + } fp; + + struct { + CfreeCgTypeId pointee; + uint32_t address_space; + } ptr; + + struct { + CfreeCgTypeId elem; + uint64_t count; + } array; + + struct { + CfreeCgTypeId ret; + CfreeCgParam* params; + uint32_t nparams; + CfreeCgCallConv call_conv; + int abi_variadic; + CfreeCgAbiAttrs ret_attrs; + } func; + + struct { + CfreeSym tag; + CgTypeField* fields; + uint32_t nfields; + int is_union; + uint32_t align_override; + uint32_t flags; + } record; + + struct { + CfreeSym tag; + CfreeCgTypeId base; + CfreeCgEnumValue* values; + uint32_t nvalues; + } enum_; + + struct { + CfreeSym name; + CfreeCgTypeId base; + } alias; + }; +}; +``` + +Exact field names can change, but the important rule is that `CgType` cannot +grow C-only semantics. + +## Public API Gaps To Address + +Before fully migrating internals, confirm the public type constructors can +represent codegen needs without C-specific escape hatches. + +Known gaps: + +- Record construction needs a forward/incomplete or begin/complete story for + self-referential records. +- Records need to distinguish struct and union. +- Packed/aligned record and field layout must be expressible. +- Bitfields need either direct representation or an explicit frontend-lowered + representation. +- Signedness should stay out of storage type identity where possible. Keep it + on integer operations, comparisons, conversions, and ABI extension attrs. +- `va_list` should be expressible as a target-provided CG type without relying + on C `Type`. + +## Migration Phases + +### Phase 1: Add CgType Registry Behind CfreeCgTypeId + +Change the existing public type registry in `src/api/cg.c` so each +`CfreeCgTypeId` resolves to a canonical `CgType`. + +During this phase, keep the legacy C type pointer as a bridge: + +```c +typedef struct CgApiType { + CgType cg; + const Type* legacy_type; /* temporary migration bridge */ +} CgApiType; +``` + +Add internal helpers: + +```c +const CgType* cg_type_get(Compiler*, CfreeCgTypeId); +uint64_t cg_type_size(Compiler*, CfreeCgTypeId); +uint32_t cg_type_align(Compiler*, CfreeCgTypeId); +int cg_type_is_int(Compiler*, CfreeCgTypeId); +int cg_type_is_float(Compiler*, CfreeCgTypeId); +int cg_type_is_ptr(Compiler*, CfreeCgTypeId); +int cg_type_is_record(Compiler*, CfreeCgTypeId); +``` + +Public query APIs such as `cfree_cg_type_size` and +`cfree_cg_type_record_field` should use `CgType`, not C `Type`. + +Deliverable: + +- No behavior change. +- Public tests still pass. +- Existing code may still use `legacy_type`, but new code should not add new + direct `Type*` usage. + +### Phase 2: Cache CgTypeId In The C Frontend Type + +Keep the C frontend `Type`, but make its lowered CG representation explicit. + +Add a cache field to `lang/c/type/type.h`: + +```c +CfreeCgTypeId cg_id; +``` + +or, if mutability of interned `Type` is undesirable, add a frontend-side +map from `Type*` to `CfreeCgTypeId`. + +Update `type_cg_id(CfreeCompiler*, const Type*)` so it constructs through the +public CG API once and returns the cached id thereafter. + +Recursive records need special handling. Prefer a real public +begin/complete/forward record API over the current placeholder behavior. + +Deliverable: + +- `lang/c` lowers to public CG type constructors. +- `src` still builds with the legacy bridge. + +### Phase 3: Migrate src/api/cg.c State To CgType + +Replace stored `const Type*` in CG state with `CfreeCgTypeId` or +`const CgType*`. + +High-priority structures: + +- stack values +- operands +- slots +- symbol type table +- function return type +- memory access descriptors +- conversion helpers +- call descriptors +- intrinsic and atomic lowering helpers + +Temporary bridge calls into old ABI/arch code are acceptable, but they should +be centralized. Do not continue storing C `Type*` in CG state. + +Deliverable: + +- `src/api/cg.c` mostly reasons in `CfreeCgTypeId`/`CgType`. +- Remaining `Type*` use is isolated to bridge functions. + +### Phase 4: Migrate ABI To CgType + +Change ABI APIs from C `Type*` to CG type handles. + +Current APIs to migrate: + +```c +ABITypeInfo abi_type_info(TargetABI*, const Type*); +u32 abi_sizeof(TargetABI*, const Type*); +u32 abi_alignof(TargetABI*, const Type*); +const ABIRecordLayout* abi_record_layout(TargetABI*, const Type*); +const ABIFuncInfo* abi_func_info(TargetABI*, const Type* fn_type); +``` + +Target APIs should look more like: + +```c +ABITypeInfo abi_type_info(TargetABI*, CfreeCgTypeId); +u32 abi_sizeof(TargetABI*, CfreeCgTypeId); +u32 abi_alignof(TargetABI*, CfreeCgTypeId); +const ABIRecordLayout* abi_record_layout(TargetABI*, CfreeCgTypeId); +const ABIFuncInfo* abi_func_info(TargetABI*, CfreeCgTypeId fn_type); +``` + +Record layout should be cached by `CfreeCgTypeId`, not `Type*`. + +Deliverable: + +- ABI no longer includes `type/type.h`. +- ABI classification uses only `CgType` and target facts. + +### Phase 5: Migrate CGTarget And Arch Lowering + +Change target-facing structs in `src/arch/arch.h` from `const Type*` to +`CfreeCgTypeId` or derived `CgType` facts. + +Most arch code only needs: + +- byte size +- alignment +- integer width +- float width +- pointer vs integer vs float +- register class +- signedness for specific signed operations + +Replace helpers such as: + +```c +type_is_64(t) +type_is_fp_double(t) +type_byte_size(t) +type_is_signed(t) +``` + +with CG-type helpers. Where signedness is operation-specific, pass it through +operation metadata instead of reading it from type identity. + +Deliverable: + +- arch code does not include `type/type.h` +- `CGTarget` is language-neutral + +### Phase 6: Migrate Optimizer And Debug Bridges + +Optimizer IR currently stores `const Type*` in several places. Move it to +`CfreeCgTypeId` or `CgType` facts. + +Debug should remain a separate concern: + +- Codegen debug emission can consume frontend-provided debug type IDs. +- C-specific debug lowering from C `Type` should live in `lang/c` or an + explicit C debug adapter, not in generic `src` code. + +Deliverable: + +- optimizer no longer includes C type headers +- generic debug producer does not depend on C type + +### Phase 7: Remove Legacy C Type Bridges + +After CG, ABI, arch, opt, and generic debug no longer need C `Type*`, remove: + +- `legacy_type` from `CgApiType` +- `cg_api_type_import` +- `cg_api_type_resolve` +- `cfree_cg_internal_*_type` +- `src/type/type.h` shim +- `src/decl/*` shims if no longer needed + +Deliverable: + +- `src` no longer depends on `lang/c/type`. +- `lang/c` is the only owner of C semantic types. + +### Phase 8: Make C A Registered Frontend + +Once `src` no longer needs C headers, make C follow the same pattern as Toy. + +Current special case: + +- `src/api/pipeline.c` directly includes `../../lang/c/c.h` +- `compile_into` has a hardcoded `CFREE_LANG_C` branch + +Target: + +- `cfree_c_compile` is registered through the same frontend mechanism as Toy. +- `src/api/pipeline.c` only calls `c->frontends[input->lang]` for language + compilation. +- Eventually replace enum-indexed registration with string registration. + +ASM can remain a builtin path temporarily, or be moved to its own registered +frontend in a later cleanup. + +Deliverable: + +- no `src -> lang/c` include +- C frontend is linked/registered the same way as Toy + +## Suggested PR Sequence + +1. Add `CgType` and registry helpers; keep legacy `Type*`. +2. Add C frontend `Type -> CfreeCgTypeId` cache. +3. Convert public type query APIs to read `CgType`. +4. Convert `src/api/cg.c` stack/operand/slot/symbol state to CG types. +5. Convert ABI to CG types. +6. Convert `CGTarget` and arch lowering to CG types. +7. Convert optimizer/debug generic paths. +8. Delete legacy bridges and shim headers. +9. Register C like Toy and remove direct `src/api/pipeline.c -> lang/c/c.h`. + +Each PR should keep `make lib` and `CFREE_TEST_ALLOW_SKIP=1 make test-parse` +green. Broader `test-cg` and arch-specific tests should be run around phases +4 through 6. + +## Non-Goals + +- Do not remove the C frontend `Type` early. It is still needed for C language + semantics. +- Do not move C-only rules into `CgType`. +- Do not make `ObjBuilder` understand type systems. +- Do not make `CGTarget` inspect language-specific types. + +## Completion Criteria + +- No `src` file includes `lang/c` headers directly or indirectly. +- No `src` file includes `type/type.h` as a C semantic type. +- Public CG type constructors create all codegen-visible type facts. +- ABI, arch, CG, and optimizer operate on `CgType`/`CfreeCgTypeId`. +- C and Toy are both registered language frontends. +- C-specific lexer, preprocessor, parser, decl, and type code live under + `lang/c` only. diff --git a/src/api/cg.c b/src/api/cg.c @@ -13,6 +13,62 @@ #include "obj/obj.h" #include "type/type.h" +typedef struct CgTypeField { + CfreeSym name; + CfreeCgTypeId type; + u64 offset; + u32 align_override; +} CgTypeField; + +typedef struct CgType { + CfreeCgTypeKind kind; + u64 size; + u32 align; + u32 pad; + union { + struct { + u32 width; + } integer; + struct { + u32 width; + } fp; + struct { + CfreeCgTypeId pointee; + u32 address_space; + } ptr; + struct { + CfreeCgTypeId elem; + u64 count; + } array; + struct { + CfreeCgTypeId ret; + CfreeCgParam* params; + u32 nparams; + CfreeCgCallConv call_conv; + int abi_variadic; + CfreeCgAbiAttrs ret_attrs; + } func; + struct { + CfreeSym tag; + CgTypeField* fields; + u32 nfields; + int is_union; + u32 align_override; + u32 flags; + } record; + struct { + CfreeSym tag; + CfreeCgTypeId base; + CfreeCgEnumValue* values; + u32 nvalues; + } enum_; + struct { + CfreeSym name; + CfreeCgTypeId base; + } alias; + }; +} CgType; + typedef enum CgApiTypeKind { CG_API_TYPE_PTR, CG_API_TYPE_ARRAY, @@ -23,6 +79,7 @@ typedef enum CgApiTypeKind { } CgApiTypeKind; typedef struct CgApiType { + CgType cg; const Type* type; CfreeCgTypeId base; CfreeSym name; @@ -45,6 +102,9 @@ SEGVEC_DEFINE(CgApiTypes, CgApiType, CG_API_TYPE_SEG_SHIFT); typedef struct CgApiState { Heap* heap; CgApiTypes types; + CgType builtins[CFREE_CG_BUILTIN_COUNT]; + u8 builtins_init; + u8 pad[3]; } CgApiState; static CfreeCgTypeId type_id_from_tuple(u32 seg, u32 index) { @@ -79,6 +139,11 @@ static CfreeCgTypeId type_id_for_user_index(u32 index) { return user_id_from_index(index); } +static u64 cg_align_to(u64 n, u32 align) { + u64 a = align ? (u64)align : 1u; + return ((n + a - 1u) / a) * a; +} + static const Type* builtin_type(Compiler* c, CfreeCgBuiltinType t) { switch (t) { case CFREE_CG_BUILTIN_VOID: @@ -107,6 +172,116 @@ static const Type* builtin_type(Compiler* c, CfreeCgBuiltinType t) { return NULL; } +static CfreeCgTypeId builtin_id_from_type_kind(TypeKind kind) { + switch (kind) { + case TY_VOID: + return builtin_id(CFREE_CG_BUILTIN_VOID); + case TY_BOOL: + return builtin_id(CFREE_CG_BUILTIN_BOOL); + case TY_CHAR: + case TY_SCHAR: + case TY_UCHAR: + return builtin_id(CFREE_CG_BUILTIN_I8); + case TY_SHORT: + case TY_USHORT: + return builtin_id(CFREE_CG_BUILTIN_I16); + case TY_INT: + case TY_UINT: + return builtin_id(CFREE_CG_BUILTIN_I32); + case TY_LONG: + case TY_ULONG: + case TY_LLONG: + case TY_ULLONG: + return builtin_id(CFREE_CG_BUILTIN_I64); + case TY_INT128: + case TY_UINT128: + return builtin_id(CFREE_CG_BUILTIN_I128); + case TY_FLOAT: + return builtin_id(CFREE_CG_BUILTIN_F32); + case TY_DOUBLE: + case TY_LDOUBLE: + return builtin_id(CFREE_CG_BUILTIN_F64); + default: + return CFREE_CG_TYPE_NONE; + } +} + +static void builtin_cg_type_init(Compiler* c, CgType* out, + CfreeCgBuiltinType t) { + memset(out, 0, sizeof(*out)); + switch (t) { + case CFREE_CG_BUILTIN_VOID: + out->kind = CFREE_CG_TYPE_VOID; + out->align = 1; + break; + case CFREE_CG_BUILTIN_BOOL: + out->kind = CFREE_CG_TYPE_BOOL; + out->size = 1; + out->align = 1; + out->integer.width = 8; + break; + case CFREE_CG_BUILTIN_I8: + out->kind = CFREE_CG_TYPE_INT; + out->size = 1; + out->align = 1; + out->integer.width = 8; + break; + case CFREE_CG_BUILTIN_I16: + out->kind = CFREE_CG_TYPE_INT; + out->size = 2; + out->align = 2; + out->integer.width = 16; + break; + case CFREE_CG_BUILTIN_I32: + out->kind = CFREE_CG_TYPE_INT; + out->size = 4; + out->align = 4; + out->integer.width = 32; + break; + case CFREE_CG_BUILTIN_I64: + out->kind = CFREE_CG_TYPE_INT; + out->size = 8; + out->align = 8; + out->integer.width = 64; + break; + case CFREE_CG_BUILTIN_I128: + out->kind = CFREE_CG_TYPE_INT; + out->size = 16; + out->align = 16; + out->integer.width = 128; + break; + case CFREE_CG_BUILTIN_F32: + out->kind = CFREE_CG_TYPE_FLOAT; + out->size = 4; + out->align = 4; + out->fp.width = 32; + break; + case CFREE_CG_BUILTIN_F64: + out->kind = CFREE_CG_TYPE_FLOAT; + out->size = 8; + out->align = 8; + out->fp.width = 64; + break; + case CFREE_CG_BUILTIN_VARARG_STATE: { + const Type* ty = builtin_type(c, t); + out->kind = CFREE_CG_TYPE_VARARG_STATE; + out->size = ty ? abi_sizeof(c->abi, ty) : 0; + out->align = ty ? abi_alignof(c->abi, ty) : 1; + break; + } + case CFREE_CG_BUILTIN_COUNT: + break; + } +} + +static void cg_api_init_builtins(Compiler* c, CgApiState* s) { + if (s->builtins_init) return; + for (u32 i = 0; i < CFREE_CG_BUILTIN_COUNT; ++i) { + builtin_cg_type_init(c, &s->builtins[i], (CfreeCgBuiltinType)i); + } + s->builtins_init = 1; +} + static CgApiState* cg_api_get(Compiler* c) { Heap* h; CgApiState* s; @@ -120,9 +295,75 @@ static CgApiState* cg_api_get(Compiler* c) { CgApiTypes_init(&s->types, h); c->cg_api = s; c->cg_api_free = cg_api_fini; + cg_api_init_builtins(c, s); return s; } +static CgApiType* api_type_from_id(Compiler* c, CfreeCgTypeId id); + +const CgType* cg_type_get(Compiler* c, CfreeCgTypeId id) { + u32 seg; + u32 off; + CgApiState* s; + CgApiType* e; + if (!c || id == CFREE_CG_TYPE_NONE) return NULL; + seg = id >> CG_API_TYPE_SEG_SHIFT; + off = id & CG_API_TYPE_SEG_MASK; + if (seg == CG_API_TYPE_BUILTIN_SEG) { + if (off >= CFREE_CG_BUILTIN_COUNT) return NULL; + s = cg_api_get(c); + if (!s) return NULL; + cg_api_init_builtins(c, s); + return &s->builtins[off]; + } + e = api_type_from_id(c, id); + return e ? &e->cg : NULL; +} + +uint64_t cg_type_size(Compiler* c, CfreeCgTypeId id) { + const CgType* ty = cg_type_get(c, id); + return ty ? ty->size : 0; +} + +uint32_t cg_type_align(Compiler* c, CfreeCgTypeId id) { + const CgType* ty = cg_type_get(c, id); + return ty ? ty->align : 0; +} + +int cg_type_is_int(Compiler* c, CfreeCgTypeId id) { + const CgType* ty = cg_type_get(c, id); + if (ty && ty->kind == CFREE_CG_TYPE_ALIAS) { + return cg_type_is_int(c, ty->alias.base); + } + return ty && (ty->kind == CFREE_CG_TYPE_INT || + ty->kind == CFREE_CG_TYPE_BOOL || + ty->kind == CFREE_CG_TYPE_ENUM); +} + +int cg_type_is_float(Compiler* c, CfreeCgTypeId id) { + const CgType* ty = cg_type_get(c, id); + if (ty && ty->kind == CFREE_CG_TYPE_ALIAS) { + return cg_type_is_float(c, ty->alias.base); + } + return ty && ty->kind == CFREE_CG_TYPE_FLOAT; +} + +int cg_type_is_ptr(Compiler* c, CfreeCgTypeId id) { + const CgType* ty = cg_type_get(c, id); + if (ty && ty->kind == CFREE_CG_TYPE_ALIAS) { + return cg_type_is_ptr(c, ty->alias.base); + } + return ty && ty->kind == CFREE_CG_TYPE_PTR; +} + +int cg_type_is_record(Compiler* c, CfreeCgTypeId id) { + const CgType* ty = cg_type_get(c, id); + if (ty && ty->kind == CFREE_CG_TYPE_ALIAS) { + return cg_type_is_record(c, ty->alias.base); + } + return ty && ty->kind == CFREE_CG_TYPE_RECORD; +} + static CgApiType* type_alloc(Compiler* c, CfreeCgTypeId* id_out) { CgApiState* s = cg_api_get(c); CgApiType* e; @@ -243,6 +484,167 @@ static CfreeCgParam* copy_cg_params(Compiler* c, const CfreeCgParam* src, return dst; } +static CgTypeField* copy_cg_fields(Compiler* c, const CfreeCgField* src, + u32 n) { + CgTypeField* dst; + if (!n) return NULL; + if (!src) return NULL; + dst = arena_array(&c->global->arena, CgTypeField, n); + if (!dst) return NULL; + memset(dst, 0, sizeof(*dst) * n); + for (u32 i = 0; i < n; ++i) { + dst[i].name = src[i].name; + dst[i].type = src[i].type; + dst[i].align_override = src[i].align_override; + } + return dst; +} + +static int cg_type_layout_record(Compiler* c, CgType* cg) { + u32 max_align = 1; + u64 size = 0; + if (!c || !cg || cg->kind != CFREE_CG_TYPE_RECORD) return 0; + if (cg->record.nfields && !cg->record.fields) return 0; + if (cg->record.is_union) { + for (u32 i = 0; i < cg->record.nfields; ++i) { + CgTypeField* f = &cg->record.fields[i]; + u64 fsize = cg_type_size(c, f->type); + u32 falign = cg_type_align(c, f->type); + if (!falign) return 0; + if (f->align_override == 1u) { + falign = 1; + } else if (f->align_override > falign) { + falign = f->align_override; + } + if (falign > max_align) max_align = falign; + if (fsize > size) size = fsize; + f->offset = 0; + } + } else { + u64 off = 0; + for (u32 i = 0; i < cg->record.nfields; ++i) { + CgTypeField* f = &cg->record.fields[i]; + u64 fsize = cg_type_size(c, f->type); + u32 falign = cg_type_align(c, f->type); + if (!falign) return 0; + if (f->align_override == 1u) { + falign = 1; + } else if (f->align_override > falign) { + falign = f->align_override; + } + if (falign > max_align) max_align = falign; + off = cg_align_to(off, falign); + f->offset = off; + off += fsize; + } + size = off; + } + if (cg->record.align_override > max_align) { + max_align = cg->record.align_override; + } + cg->align = max_align; + cg->size = cg_align_to(size, max_align); + return 1; +} + +static int cg_type_set_ptr(Compiler* c, CgApiType* e, CfreeCgTypeId pointee, + u32 address_space) { + u32 ptr_size; + u32 ptr_align; + if (!cg_type_get(c, pointee)) return 0; + memset(&e->cg, 0, sizeof(e->cg)); + ptr_size = c->target.ptr_size ? c->target.ptr_size : 8; + ptr_align = c->target.ptr_align ? c->target.ptr_align : ptr_size; + e->cg.kind = CFREE_CG_TYPE_PTR; + e->cg.size = ptr_size; + e->cg.align = ptr_align; + e->cg.ptr.pointee = pointee; + e->cg.ptr.address_space = address_space; + return 1; +} + +static int cg_type_set_array(Compiler* c, CgApiType* e, CfreeCgTypeId elem, + u64 count) { + const CgType* ety = cg_type_get(c, elem); + if (!ety) return 0; + memset(&e->cg, 0, sizeof(e->cg)); + e->cg.kind = CFREE_CG_TYPE_ARRAY; + e->cg.size = ety->size * count; + e->cg.align = ety->align; + e->cg.array.elem = elem; + e->cg.array.count = count; + return 1; +} + +static int cg_type_set_alias(Compiler* c, CgApiType* e, CfreeSym name, + CfreeCgTypeId base) { + const CgType* bty = cg_type_get(c, base); + if (!bty) return 0; + memset(&e->cg, 0, sizeof(e->cg)); + e->cg.kind = CFREE_CG_TYPE_ALIAS; + e->cg.size = bty->size; + e->cg.align = bty->align; + e->cg.alias.name = name; + e->cg.alias.base = base; + return 1; +} + +static int cg_type_set_record(Compiler* c, CgApiType* e, CfreeSym tag, + const CfreeCgField* fields, u32 nfields, + int is_union, u32 align_override, u32 flags) { + CgTypeField* copied = copy_cg_fields(c, fields, nfields); + if (nfields && !copied) return 0; + memset(&e->cg, 0, sizeof(e->cg)); + e->cg.kind = CFREE_CG_TYPE_RECORD; + e->cg.record.tag = tag; + e->cg.record.fields = copied; + e->cg.record.nfields = nfields; + e->cg.record.is_union = is_union != 0; + e->cg.record.align_override = align_override; + e->cg.record.flags = flags; + return cg_type_layout_record(c, &e->cg); +} + +static int cg_type_set_enum(Compiler* c, CgApiType* e, CfreeSym tag, + CfreeCgTypeId base, CfreeCgEnumValue* values, + u32 nvalues) { + const CgType* bty; + if (base == CFREE_CG_TYPE_NONE) base = builtin_id(CFREE_CG_BUILTIN_I32); + bty = cg_type_get(c, base); + if (!bty || !(bty->kind == CFREE_CG_TYPE_INT || + bty->kind == CFREE_CG_TYPE_BOOL)) { + return 0; + } + memset(&e->cg, 0, sizeof(e->cg)); + e->cg.kind = CFREE_CG_TYPE_ENUM; + e->cg.size = bty->size; + e->cg.align = bty->align; + e->cg.enum_.tag = tag; + e->cg.enum_.base = base; + e->cg.enum_.values = values; + e->cg.enum_.nvalues = nvalues; + return 1; +} + +static int cg_type_set_func(Compiler* c, CgApiType* e, CfreeCgFuncSig sig, + CfreeCgParam* params) { + if (!cg_type_get(c, sig.ret)) return 0; + for (u32 i = 0; i < sig.nparams; ++i) { + if (!cg_type_get(c, sig.params[i].type)) return 0; + } + memset(&e->cg, 0, sizeof(e->cg)); + e->cg.kind = CFREE_CG_TYPE_FUNC; + e->cg.size = 1; + e->cg.align = 1; + e->cg.func.ret = sig.ret; + e->cg.func.params = params; + e->cg.func.nparams = sig.nparams; + e->cg.func.call_conv = sig.call_conv; + e->cg.func.abi_variadic = sig.abi_variadic != 0; + e->cg.func.ret_attrs = sig.ret_attrs; + return 1; +} + CfreeCgBuiltinTypes cfree_cg_builtin_types(CfreeCompiler* c) { CfreeCgBuiltinTypes out; (void)c; @@ -267,7 +669,10 @@ CfreeCgTypeId cfree_cg_type_ptr(CfreeCompiler* c, CfreeCgTypeId pointee, e->base = pointee; e->address_space = address_space; e->kind = CG_API_TYPE_PTR; - return e->type ? id : CFREE_CG_TYPE_NONE; + if (!e->type || !cg_type_set_ptr(c, e, pointee, address_space)) { + return CFREE_CG_TYPE_NONE; + } + return id; } CfreeCgTypeId cfree_cg_type_array(CfreeCompiler* c, CfreeCgTypeId elem, @@ -284,7 +689,10 @@ CfreeCgTypeId cfree_cg_type_array(CfreeCompiler* c, CfreeCgTypeId elem, e->base = elem; e->array_count = count; e->kind = CG_API_TYPE_ARRAY; - return e->type ? id : CFREE_CG_TYPE_NONE; + if (!e->type || !cg_type_set_array(c, e, elem, count)) { + return CFREE_CG_TYPE_NONE; + } + return id; } CfreeCgTypeId cfree_cg_type_alias(CfreeCompiler* c, CfreeSym name, @@ -299,7 +707,7 @@ CfreeCgTypeId cfree_cg_type_alias(CfreeCompiler* c, CfreeSym name, e->base = base; e->name = name; e->kind = CG_API_TYPE_ALIAS; - return id; + return cg_type_set_alias(c, e, name, base) ? id : CFREE_CG_TYPE_NONE; } CfreeCgTypeId cfree_cg_type_record(CfreeCompiler* c, CfreeSym tag, @@ -344,7 +752,10 @@ CfreeCgTypeId cfree_cg_type_record(CfreeCompiler* c, CfreeSym tag, e->count = nfields; e->fields = copied; e->kind = CG_API_TYPE_RECORD; - return e->type ? id : CFREE_CG_TYPE_NONE; + if (!e->type || !cg_type_set_record(c, e, tag, fields, nfields, 0, 0, 0)) { + return CFREE_CG_TYPE_NONE; + } + return id; } CfreeCgTypeId cfree_cg_type_enum(CfreeCompiler* c, CfreeSym tag, @@ -375,7 +786,10 @@ CfreeCgTypeId cfree_cg_type_enum(CfreeCompiler* c, CfreeSym tag, e->count = nvalues; e->values = copied; e->kind = CG_API_TYPE_ENUM; - return e->type ? id : CFREE_CG_TYPE_NONE; + if (!e->type || !cg_type_set_enum(c, e, tag, base, copied, nvalues)) { + return CFREE_CG_TYPE_NONE; + } + return id; } CfreeCgTypeId cfree_cg_type_func(CfreeCompiler* c, CfreeCgFuncSig sig) { @@ -423,7 +837,10 @@ CfreeCgTypeId cfree_cg_type_func(CfreeCompiler* c, CfreeCgFuncSig sig) { e->call_conv = sig.call_conv; e->abi_variadic = sig.abi_variadic != 0; e->kind = CG_API_TYPE_FUNC; - return e->type ? id : CFREE_CG_TYPE_NONE; + if (!e->type || !cg_type_set_func(c, e, sig, copied)) { + return CFREE_CG_TYPE_NONE; + } + return id; } CfreeCgTypeId cg_api_type_import(Compiler* c, const Type* ty) { @@ -478,33 +895,84 @@ CfreeCgTypeId cg_api_type_import(Compiler* c, const Type* ty) { case TY_PTR: e->base = cg_api_type_import(c, ty->ptr.pointee); e->kind = CG_API_TYPE_PTR; + if (!cg_type_set_ptr(c, e, e->base, 0)) return CFREE_CG_TYPE_NONE; break; case TY_ARRAY: e->base = cg_api_type_import(c, ty->arr.elem); e->array_count = ty->arr.count; e->kind = CG_API_TYPE_ARRAY; + if (!cg_type_set_array(c, e, e->base, e->array_count)) { + return CFREE_CG_TYPE_NONE; + } break; - case TY_FUNC: + case TY_FUNC: { + CfreeCgParam* params = NULL; + CfreeCgFuncSig sig; e->base = cg_api_type_import(c, ty->fn.ret); e->count = ty->fn.nparams; e->abi_variadic = ty->fn.variadic; e->call_conv = CFREE_CG_CC_TARGET_C; e->kind = CG_API_TYPE_FUNC; + memset(&sig, 0, sizeof(sig)); + sig.ret = e->base; + sig.nparams = ty->fn.nparams; + sig.abi_variadic = ty->fn.variadic; + sig.call_conv = CFREE_CG_CC_TARGET_C; + if (ty->fn.nparams) { + params = arena_zarray(&c->global->arena, CfreeCgParam, ty->fn.nparams); + if (!params) return CFREE_CG_TYPE_NONE; + for (u32 i = 0; i < ty->fn.nparams; ++i) { + params[i].type = cg_api_type_import(c, ty->fn.params[i]); + if (params[i].type == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; + } + } + sig.params = params; + e->params = params; + if (!cg_type_set_func(c, e, sig, params)) return CFREE_CG_TYPE_NONE; break; + } case TY_STRUCT: - case TY_UNION: + case TY_UNION: { + CfreeCgField* fields = NULL; e->name = ty->rec.tag; e->count = ty->rec.nfields; e->kind = CG_API_TYPE_RECORD; + e->cg.kind = CFREE_CG_TYPE_RECORD; + if (ty->rec.nfields) { + fields = arena_zarray(&c->global->arena, CfreeCgField, ty->rec.nfields); + if (!fields) return CFREE_CG_TYPE_NONE; + for (u32 i = 0; i < ty->rec.nfields; ++i) { + const Field* f = &ty->rec.fields[i]; + fields[i].name = f->name; + fields[i].type = cg_api_type_import(c, f->type); + if (fields[i].type == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; + fields[i].align_override = + (ty->rec.packed || f->packed) ? 1u : f->align_override; + } + } + e->fields = fields; + if (!cg_type_set_record(c, e, ty->rec.tag, fields, ty->rec.nfields, + ty->kind == TY_UNION, ty->rec.align_override, + 0)) { + return CFREE_CG_TYPE_NONE; + } break; + } case TY_ENUM: e->name = ty->enm.tag; e->base = cg_api_type_import(c, ty->enm.base); e->kind = CG_API_TYPE_ENUM; + if (!cg_type_set_enum(c, e, ty->enm.tag, e->base, NULL, 0)) { + return CFREE_CG_TYPE_NONE; + } break; default: + { + CfreeCgTypeId base_id = builtin_id_from_type_kind((TypeKind)ty->kind); e->kind = CG_API_TYPE_ALIAS; + if (!cg_type_set_alias(c, e, 0, base_id)) return CFREE_CG_TYPE_NONE; break; + } } return id; } @@ -514,149 +982,128 @@ const Type* cg_api_type_resolve(Compiler* c, CfreeCgTypeId 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; + return cg_type_size(c, id); } 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; -} - -static CfreeCgTypeKind api_type_kind_from_type(const Type* ty) { - if (!ty) return CFREE_CG_TYPE_VOID; - switch (ty->kind) { - case TY_VOID: - return CFREE_CG_TYPE_VOID; - case TY_BOOL: - return CFREE_CG_TYPE_BOOL; - case TY_FLOAT: - case TY_DOUBLE: - case TY_LDOUBLE: - return CFREE_CG_TYPE_FLOAT; - case TY_PTR: - return CFREE_CG_TYPE_PTR; - case TY_ARRAY: - return CFREE_CG_TYPE_ARRAY; - case TY_FUNC: - return CFREE_CG_TYPE_FUNC; - case TY_STRUCT: - case TY_UNION: - return CFREE_CG_TYPE_RECORD; - case TY_ENUM: - return CFREE_CG_TYPE_ENUM; - default: - return type_is_int(ty) ? CFREE_CG_TYPE_INT : CFREE_CG_TYPE_VOID; - } + return cg_type_align(c, id); } CfreeCgTypeKind cfree_cg_type_kind(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); - if (e) { - if (e->kind == CG_API_TYPE_ALIAS) return CFREE_CG_TYPE_ALIAS; - if (e->kind == CG_API_TYPE_PTR) return CFREE_CG_TYPE_PTR; - if (e->kind == CG_API_TYPE_ARRAY) return CFREE_CG_TYPE_ARRAY; - if (e->kind == CG_API_TYPE_FUNC) return CFREE_CG_TYPE_FUNC; - if (e->kind == CG_API_TYPE_RECORD) return CFREE_CG_TYPE_RECORD; - if (e->kind == CG_API_TYPE_ENUM) return CFREE_CG_TYPE_ENUM; - } - if (id == builtin_id(CFREE_CG_BUILTIN_VARARG_STATE)) { - return CFREE_CG_TYPE_VARARG_STATE; - } - return api_type_kind_from_type(resolve_type(c, id)); + const CgType* ty = cg_type_get(c, id); + return ty ? ty->kind : CFREE_CG_TYPE_VOID; } uint32_t cfree_cg_type_int_width(CfreeCompiler* c, CfreeCgTypeId id) { - const Type* ty = resolve_type(c, id); - return (ty && type_is_int(ty)) ? (uint32_t)abi_sizeof(c->abi, ty) * 8u : 0u; + const CgType* ty = cg_type_get(c, id); + if (!ty) return 0; + if (ty->kind == CFREE_CG_TYPE_INT || ty->kind == CFREE_CG_TYPE_BOOL) { + return ty->integer.width; + } + if (ty->kind == CFREE_CG_TYPE_ENUM) { + return (uint32_t)ty->size * 8u; + } + if (ty->kind == CFREE_CG_TYPE_ALIAS) { + return cfree_cg_type_int_width(c, ty->alias.base); + } + return 0; } uint32_t cfree_cg_type_float_width(CfreeCompiler* c, CfreeCgTypeId id) { - const Type* ty = resolve_type(c, id); + const CgType* ty = cg_type_get(c, id); if (!ty) return 0; - if (ty->kind == TY_FLOAT || ty->kind == TY_DOUBLE || ty->kind == TY_LDOUBLE) { - return (uint32_t)abi_sizeof(c->abi, ty) * 8u; + if (ty->kind == CFREE_CG_TYPE_FLOAT) return ty->fp.width; + if (ty->kind == CFREE_CG_TYPE_ALIAS) { + return cfree_cg_type_float_width(c, ty->alias.base); } return 0; } 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; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_PTR) ? ty->ptr.pointee + : CFREE_CG_TYPE_NONE; } CfreeCgTypeId cfree_cg_type_array_elem(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); - return (e && e->kind == CG_API_TYPE_ARRAY) ? e->base : CFREE_CG_TYPE_NONE; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_ARRAY) ? ty->array.elem + : CFREE_CG_TYPE_NONE; } uint32_t cfree_cg_type_ptr_address_space(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); - return (e && e->kind == CG_API_TYPE_PTR) ? e->address_space : 0u; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_PTR) ? ty->ptr.address_space : 0u; } uint64_t cfree_cg_type_array_count(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); - return (e && e->kind == CG_API_TYPE_ARRAY) ? e->array_count : 0u; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_ARRAY) ? ty->array.count : 0u; } 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; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_FUNC) ? ty->func.ret + : 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; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_FUNC) ? ty->func.nparams : 0; } CfreeCgAbiAttrs cfree_cg_type_func_ret_attrs(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); + const CgType* ty = cg_type_get(c, id); CfreeCgAbiAttrs empty; memset(&empty, 0, sizeof(empty)); - return (e && e->kind == CG_API_TYPE_FUNC) ? e->ret_attrs : empty; + return (ty && ty->kind == CFREE_CG_TYPE_FUNC) ? ty->func.ret_attrs : empty; } CfreeCgParam cfree_cg_type_func_param(CfreeCompiler* c, CfreeCgTypeId id, uint32_t index) { - CgApiType* e = api_type_from_id(c, id); + const CgType* ty = cg_type_get(c, id); CfreeCgParam empty; memset(&empty, 0, sizeof(empty)); - if (!e || e->kind != CG_API_TYPE_FUNC || index >= e->count) return empty; - return e->params[index]; + if (!ty || ty->kind != CFREE_CG_TYPE_FUNC || index >= ty->func.nparams) { + return empty; + } + return ty->func.params[index]; } CfreeCgCallConv cfree_cg_type_func_call_conv(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); - return (e && e->kind == CG_API_TYPE_FUNC) ? e->call_conv - : CFREE_CG_CC_TARGET_C; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_FUNC) ? ty->func.call_conv + : CFREE_CG_CC_TARGET_C; } int cfree_cg_type_func_is_variadic(CfreeCompiler* c, CfreeCgTypeId id) { - CgApiType* e = api_type_from_id(c, id); - return e && e->kind == CG_API_TYPE_FUNC && e->abi_variadic; + const CgType* ty = cg_type_get(c, id); + return ty && ty->kind == CFREE_CG_TYPE_FUNC && ty->func.abi_variadic; } 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; + const CgType* ty = cg_type_get(c, id); + return (ty && ty->kind == CFREE_CG_TYPE_RECORD) ? ty->record.nfields : 0; } int cfree_cg_type_record_field(CfreeCompiler* c, CfreeCgTypeId id, uint32_t index, CfreeCgField* out, uint64_t* offset_out) { - CgApiType* e = api_type_from_id(c, id); - const ABIRecordLayout* layout; - if (!e || e->kind != CG_API_TYPE_RECORD || index >= e->count) return 1; - if (out) *out = e->fields[index]; - if (offset_out) { - layout = abi_record_layout(c->abi, e->type); - *offset_out = (layout && index < layout->nfields) - ? (uint64_t)layout->fields[index].offset - : 0u; + const CgType* ty = cg_type_get(c, id); + const CgTypeField* f; + if (!ty || ty->kind != CFREE_CG_TYPE_RECORD || + index >= ty->record.nfields) { + return 1; + } + f = &ty->record.fields[index]; + if (out) { + out->name = f->name; + out->type = f->type; + out->align_override = f->align_override; } + if (offset_out) *offset_out = f->offset; return 0; } diff --git a/src/api/cg_api.h b/src/api/cg_api.h @@ -8,6 +8,7 @@ typedef struct CGTarget CGTarget; typedef struct MCEmitter MCEmitter; +typedef struct CgType CgType; typedef uint32_t ObjSymId; enum { @@ -20,6 +21,13 @@ enum { const Type* cg_api_type_resolve(Compiler*, CfreeCgTypeId); CfreeCgTypeId cg_api_type_import(Compiler*, const Type*); +const CgType* cg_type_get(Compiler*, CfreeCgTypeId); +uint64_t cg_type_size(Compiler*, CfreeCgTypeId); +uint32_t cg_type_align(Compiler*, CfreeCgTypeId); +int cg_type_is_int(Compiler*, CfreeCgTypeId); +int cg_type_is_float(Compiler*, CfreeCgTypeId); +int cg_type_is_ptr(Compiler*, CfreeCgTypeId); +int cg_type_is_record(Compiler*, CfreeCgTypeId); Compiler* cfree_cg_internal_compiler(CfreeCg*); CGTarget* cfree_cg_internal_target(CfreeCg*); MCEmitter* cfree_cg_internal_mc(CfreeCg*); diff --git a/test/api/cg_type_test.c b/test/api/cg_type_test.c @@ -61,14 +61,16 @@ int main(void) { CfreeCgTypeId va_list_ty; CfreeCgTypeId ptr_i32; CfreeCgTypeId array_i32; - CfreeCgTypeId params[2]; + CfreeCgParam params[2]; + CfreeCgFuncSig sig; CfreeCgTypeId fn; CfreeCgTypeId alias; - CfreeCgTypeId qual; CfreeCgTypeId rec; CfreeCgTypeId enm; CfreeCgField fields[2]; + CfreeCgField field_out; CfreeCgEnumValue vals[2]; + uint64_t field_off; memset(&target, 0, sizeof(target)); target.arch = CFREE_ARCH_ARM_64; @@ -103,29 +105,32 @@ int main(void) { EXPECT(va_list_ty != CFREE_CG_TYPE_NONE, "va_list builtin id is none"); EXPECT(void_ty != i32_ty && i32_ty != f64_ty, "builtin ids collide"); - ptr_i32 = cfree_cg_type_ptr(c, i32_ty); + ptr_i32 = cfree_cg_type_ptr(c, i32_ty, 0); array_i32 = cfree_cg_type_array(c, i32_ty, 4); EXPECT(ptr_i32 != CFREE_CG_TYPE_NONE, "ptr type failed"); EXPECT(array_i32 != CFREE_CG_TYPE_NONE, "array type failed"); - EXPECT(cfree_cg_type_ptr(c, i32_ty) == ptr_i32, + EXPECT(cfree_cg_type_ptr(c, i32_ty, 0) == ptr_i32, "ptr type id should be interned"); EXPECT(cfree_cg_type_array(c, i32_ty, 4) == array_i32, "array type id should be interned"); - EXPECT(cfree_cg_type_ptr(c, CFREE_CG_TYPE_NONE) == CFREE_CG_TYPE_NONE, + EXPECT(cfree_cg_type_ptr(c, CFREE_CG_TYPE_NONE, 0) == CFREE_CG_TYPE_NONE, "invalid pointer pointee should fail"); + EXPECT(cfree_cg_type_size(c, ptr_i32) == 8, "ptr size mismatch"); + EXPECT(cfree_cg_type_align(c, ptr_i32) == 8, "ptr align mismatch"); + EXPECT(cfree_cg_type_ptr_pointee(c, ptr_i32) == i32_ty, + "ptr pointee mismatch"); + EXPECT(cfree_cg_type_array_elem(c, array_i32) == i32_ty, + "array elem mismatch"); + EXPECT(cfree_cg_type_array_count(c, array_i32) == 4, + "array count mismatch"); alias = cfree_cg_type_alias(c, cfree_sym_intern(c, "I"), i32_ty); - qual = cfree_cg_type_qualified(c, alias, CFREE_CG_TQ_CONST); EXPECT(alias != CFREE_CG_TYPE_NONE && alias != i32_ty, "alias id should be fresh"); - EXPECT(qual != CFREE_CG_TYPE_NONE && qual != alias, - "qualified type failed"); - EXPECT(cfree_cg_type_qualified(c, alias, CFREE_CG_TQ_CONST) == qual, - "qualified type id should be interned"); - EXPECT(cfree_cg_type_qualified(c, alias, 0) == alias, - "zero qualifiers should return base id"); - EXPECT(cfree_cg_type_qualified(c, alias, 1u << 12) == CFREE_CG_TYPE_NONE, - "unknown qualifier bit should fail"); + EXPECT(cfree_cg_type_kind(c, alias) == CFREE_CG_TYPE_ALIAS, + "alias kind mismatch"); + EXPECT(cfree_cg_type_size(c, alias) == cfree_cg_type_size(c, i32_ty), + "alias size mismatch"); fields[0].name = cfree_sym_intern(c, "a"); fields[0].type = i32_ty; @@ -138,6 +143,15 @@ int main(void) { EXPECT(cfree_cg_type_record(c, cfree_sym_intern(c, "Bad"), fields, 0) != CFREE_CG_TYPE_NONE, "empty record type failed"); + EXPECT(cfree_cg_type_kind(c, rec) == CFREE_CG_TYPE_RECORD, + "record kind mismatch"); + EXPECT(cfree_cg_type_record_nfields(c, rec) == 2, + "record field count mismatch"); + EXPECT(cfree_cg_type_record_field(c, rec, 1, &field_out, &field_off) == 0, + "record field query failed"); + EXPECT(field_out.name == fields[1].name && field_out.type == ptr_i32, + "record field data mismatch"); + EXPECT(field_off == 4, "record field offset mismatch"); vals[0].name = cfree_sym_intern(c, "A"); vals[0].value = 1; @@ -147,13 +161,26 @@ int main(void) { vals, 2); EXPECT(enm != CFREE_CG_TYPE_NONE, "enum type failed"); - params[0] = i32_ty; - params[1] = ptr_i32; - fn = cfree_cg_type_func(c, i64_ty, params, 2, 1); + memset(params, 0, sizeof(params)); + params[0].type = i32_ty; + params[1].type = ptr_i32; + memset(&sig, 0, sizeof(sig)); + sig.ret = i64_ty; + sig.params = params; + sig.nparams = 2; + sig.abi_variadic = 1; + sig.call_conv = CFREE_CG_CC_TARGET_C; + fn = cfree_cg_type_func(c, sig); EXPECT(fn != CFREE_CG_TYPE_NONE, "function type failed"); - EXPECT(cfree_cg_type_func(c, i64_ty, params, 2, 1) == fn, + EXPECT(cfree_cg_type_func(c, sig) == fn, "function type id should be interned"); - EXPECT(cfree_cg_type_func(c, i64_ty, params, 70000, 0) == CFREE_CG_TYPE_NONE, + EXPECT(cfree_cg_type_func_ret(c, fn) == i64_ty, "function return mismatch"); + EXPECT(cfree_cg_type_func_nparams(c, fn) == 2, + "function param count mismatch"); + EXPECT(cfree_cg_type_func_param(c, fn, 1).type == ptr_i32, + "function param mismatch"); + sig.nparams = 70000; + EXPECT(cfree_cg_type_func(c, sig) == CFREE_CG_TYPE_NONE, "oversized function param list should fail"); cfree_compiler_free(c);