commit 64afd07c476c8aaca38a511fdc9177b830390630
parent 0f9b477324a56ef9ee19c54b592b703c5d5796e6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 15 Jun 2026 12:56:01 -0700
cg: clean cutover of the type and type-id system
Replace the segmented KitCgTypeId scheme and NONE-as-void convention with a
direct-index type table per doc/plan/CG-TYPES.md.
Core (src/cg/type.c, include/kit/cg.h):
- Builtin ids are 1..KIT_CG_BUILTIN_COUNT; user ids index directly after them;
no segment/bias decode.
- Void is always the void builtin; KIT_CG_TYPE_NONE means invalid/absent only.
Function results are always valid type ids (kit_cg_func_result_has_value /
kit_cg_type_is_void replace result.type == NONE checks).
- Two-phase nominal records: kit_cg_type_record_decl + _record_complete (once);
pointers to incomplete records are legal; by-value fields must be sized.
- Layout/size/align come from the ABI path and cache on the type entry.
- ptr/array/func interning via id-keyed maps; exact vs storage alias queries.
C frontend (lang/c):
- Records lower via declare-at-tag / complete-at-definition; the old
record-context pointer->void* erasure and incomplete->void lowering are gone.
- A pointer carries its pointee's nominal (decl) id and never forces
completion, so a forward-declared struct used only through pointers is never
completed; completion is triggered only by by-value use (local, by-value
field/param/result, array element, sizeof, member access).
- In-flight-record guard breaks any by-value re-entry cycle (invalid C) with a
clean error instead of a stack overflow.
- Diagnose "array has incomplete element type" at array-declarator formation
(6.7.6.2p1), matching clang across member/param/extern/local/typedef; VLAs
are not affected.
Toy and wasm frontends use the void builtin for void results.
Backend/library fixes uncovered by the void = builtin invariant:
- c_target c_emit_ret: detect non-void via cg_type_is_void, not != NONE, so a
void return emits `return;` rather than __builtin_unreachable().
- cg/atomic.c: the void __atomic_store_8 libcall result is the void builtin,
not NONE (kit_cg_type_func now rejects a NONE result; reachable on 32-bit
targets doing 8-byte atomics).
Validation: test-parse (DREJC + R + err), test-toy, test-cg-api, test-opt,
test-aa64-inline, test-debug, test-dwarf, smoke x64/rv64 all green.
Diffstat:
50 files changed, 4205 insertions(+), 864 deletions(-)
diff --git a/doc/plan/CG-TYPES.md b/doc/plan/CG-TYPES.md
@@ -0,0 +1,586 @@
+# CG type and type-id system clean cutover
+
+**Goal.** Replace the current `KitCgTypeId` implementation and public type API
+with a simpler, explicit, and ABI-correct type table. Keep the good part: a
+small compiler-owned integer handle that can be stored cheaply throughout IR,
+ABI records, frontends, and backends. Change what that handle means, how void
+and aliases are represented, where layout facts are computed, and how nominal
+records are completed.
+
+This is a **clean-break rewrite, no backcompat**. The old constructors, query
+semantics, `KIT_CG_TYPE_NONE`-as-void convention, segmented id encoding, and
+frontend workarounds are removed as the cutover lands. There is no compatibility
+adapter and no byte-identity gate.
+
+---
+
+## 1. Current State
+
+The public API uses an opaque `uint32_t KitCgTypeId`:
+
+- `0` is `KIT_CG_TYPE_NONE`.
+- Builtins are encoded in a reserved id segment.
+- User types are stored in a segmented vector with a biased segment id.
+- Pointers, arrays, and functions are structurally interned.
+- Aliases, records, and enums allocate fresh ids.
+
+Internally, `src/cg/type.c` owns `CgApiState`:
+
+- `CgType builtins[KIT_CG_BUILTIN_COUNT]`
+- `CgApiTypes types`
+- structural indexes for pointer, array, and function types
+- per-entry caches for alias resolution, predicates, type class, and ABI layout
+
+The C frontend lowers `Type*` to `KitCgTypeId` lazily and caches ids on C type
+nodes and parser stack slots. Incomplete records are handled outside CG: a
+temporarily incomplete record lowers to `void`, and record-field/function-param
+pointer lowering collapses pointees to `void*` so layout can proceed without a
+true incomplete nominal record id.
+
+The ABI layer reads `CgType` entries and caches target layout/classification
+facts by type id. Some storage facts live eagerly on `CgType` (`size`, `align`);
+some target facts live in `TargetABI`; record layout is currently computed by
+the CG type constructor and re-exposed by the ABI cache.
+
+---
+
+## 2. Problems
+
+### 2.1 `NONE` Has Too Many Meanings
+
+`KIT_CG_TYPE_NONE` currently means:
+
+- invalid type id
+- absent function result, i.e. a void return
+- statement scope or no value in some control APIs
+
+There is also a real void builtin. Every caller must remember which API treats
+void as a real type and which treats `NONE` as absence. That is a correctness
+hazard and a permanent source of branchy special cases.
+
+### 2.2 Alias Semantics Are Implicit
+
+Some queries inspect the exact id. Others recurse through aliases. Examples:
+
+- integer and float width follow aliases
+- predicates follow aliases through a cached descriptor
+- pointer/array/function field queries are mostly exact-kind queries
+- many internal call sites remember to call `api_unalias_type`
+
+The result is not one type model; it is a collection of local conventions.
+
+### 2.3 ID Decode Complexity Does Not Buy Enough
+
+The segmented builtin/user encoding makes every id lookup decode segment bits.
+That helped keep builtin ids stable and user pointers stable, but the hot path
+now contains extra decode logic and special builtin handling. A direct table
+index keeps the same public handle shape with less machinery.
+
+### 2.4 Layout Authority Is Split
+
+`CgType` eagerly stores `size` and `align`, while `TargetABI` is supposed to be
+the single authority for target-dependent layout and calling convention facts.
+This is especially fragile for:
+
+- 32-bit ABIs and non-default pointer alignment
+- Windows and other ABI-specific scalar/aggregate rules
+- bit-field layout differences
+- `va_list` and other target-shaped builtins
+
+### 2.5 Incomplete Nominal Records Are Not Modeled
+
+The C frontend needs true identities for incomplete records so these are legal:
+
+```c
+struct S;
+struct T { struct S *p; };
+struct S { int x; };
+```
+
+Today CG has no incomplete record declaration/completion API, so the frontend
+works around it by erasing some pointee identity to `void*` while lowering
+record fields and function parameters. That preserves layout but loses type
+identity and pushes a CG responsibility into the frontend.
+
+### 2.6 Queries Are Too Fragmented
+
+The public API exposes many one-off queries (`kind`, `size`, `align`,
+`ptr_pointee`, `array_elem`, `func_param`, `record_field`, etc.). That is easy
+to call but expensive and ambiguous at scale: each query chooses exact vs
+resolved behavior separately, and hot call sites decode the same id repeatedly.
+
+---
+
+## 3. Design Decisions
+
+| # | Decision | Choice |
+|---|---|---|
+| D1 | Public handle shape | Keep `typedef uint32_t KitCgTypeId`; `0` is invalid/none only |
+| D2 | Void representation | Void is always the real void builtin type id; `NONE` never means void |
+| D3 | ID layout | Builtin ids are `1..KIT_CG_BUILTIN_COUNT`; user ids are `KIT_CG_BUILTIN_COUNT + 1 + index` |
+| D4 | Cross-compiler stability | Type ids are stable only within one `KitCompiler`; never serialized as semantic identities |
+| D5 | Structural interning | Pointer, array, and function constructors intern by exact constructor shape |
+| D6 | Nominal identity | Records, enums, and aliases keep fresh source-facing ids |
+| D7 | Alias behavior | Exact queries inspect aliases; storage/semantic queries resolve aliases explicitly |
+| D8 | Function void result | Function result type is always a valid type id; use the void builtin for no value |
+| D9 | Layout authority | `TargetABI` computes all size/align/scalar layout facts; type entries only cache ABI results |
+| D10 | Records | Records are nominal and two-phase: declare first, complete once |
+| D11 | Incomplete use | Pointers to incomplete records are legal; sizing, arrays, fields, by-value params/results, locals, and memory accesses require complete/sized types |
+| D12 | Caches | Caches live on the owning type entry or ABI context and are filled only after the facts they depend on are final |
+| D13 | Implementation style | C11, no VLAs, no global type state; everything hangs off `KitCompiler`/`TargetABI` |
+
+---
+
+## 4. New Public Shape
+
+### 4.1 IDs and Builtins
+
+The id remains opaque, but implementation decode is direct:
+
+```c
+#define KIT_CG_TYPE_NONE 0u
+
+/* implementation rule, not a public promise beyond nonzero/stable per compiler:
+ * builtin id = 1 + KitCgBuiltinType
+ * user id = 1 + KIT_CG_BUILTIN_COUNT + user_index
+ */
+```
+
+The public builtin query becomes singular. The whole-table query is removed
+unless a real caller needs it after the cutover.
+
+```c
+KIT_API KitCgTypeId kit_cg_type_builtin(KitCompiler*, KitCgBuiltinType);
+```
+
+### 4.2 Function Signatures
+
+`KitCgFuncResult.type` is always valid. A void function uses the void builtin.
+
+```c
+typedef struct KitCgFuncResult {
+ KitCgTypeId type; /* valid; void builtin means no value */
+ KitCgAbiAttrs attrs;
+} KitCgFuncResult;
+
+typedef struct KitCgFuncSig {
+ KitCgFuncResult result;
+ const KitCgFuncParam* params;
+ uint32_t nparams;
+ KitCgCallConv call_conv;
+ bool abi_variadic;
+} KitCgFuncSig;
+```
+
+The helper predicate is explicit:
+
+```c
+KIT_API int kit_cg_type_is_void(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_func_result_has_value(KitCompiler*, KitCgFuncResult);
+```
+
+No caller tests `result.type == KIT_CG_TYPE_NONE` after this cutover.
+
+### 4.3 Exact and Storage Queries
+
+Exact identity and storage identity are separate operations.
+
+```c
+KIT_API KitCgTypeId kit_cg_type_resolve_alias(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_type_same_storage(KitCompiler*, KitCgTypeId,
+ KitCgTypeId);
+```
+
+Rules:
+
+- `kit_cg_type_kind` is exact. An alias id reports `KIT_CG_TYPE_ALIAS`.
+- `kit_cg_type_storage_info` resolves aliases before reporting layout/scalar
+ facts.
+- `kit_cg_type_same_storage` recursively compares alias-resolved storage
+ structure where needed, so `ptr(alias(i32))` and `ptr(i32)` can be storage
+ equivalent even if their exact ids differ.
+
+### 4.4 Descriptor Query
+
+Add one structured query for hot callers and keep narrow helpers only where they
+remain clearly useful.
+
+```c
+typedef enum KitCgTypeFlag {
+ KIT_CG_TYPEF_COMPLETE = 1u << 0,
+ KIT_CG_TYPEF_SIZED = 1u << 1,
+ KIT_CG_TYPEF_BUILTIN = 1u << 2,
+ KIT_CG_TYPEF_NOMINAL = 1u << 3,
+} KitCgTypeFlag;
+
+typedef enum KitCgStorageKind {
+ KIT_CG_STORAGE_VOID,
+ KIT_CG_STORAGE_BOOL,
+ KIT_CG_STORAGE_INT,
+ KIT_CG_STORAGE_FLOAT,
+ KIT_CG_STORAGE_PTR,
+ KIT_CG_STORAGE_AGGREGATE,
+} KitCgStorageKind;
+
+typedef struct KitCgTypeLayout {
+ uint64_t size;
+ uint32_t align;
+ uint16_t scalar_width;
+ uint8_t storage_kind; /* KitCgStorageKind */
+ uint8_t valid;
+} KitCgTypeLayout;
+
+typedef struct KitCgTypeInfo {
+ KitCgTypeId id;
+ KitCgTypeId storage_id; /* alias-resolved id, or id */
+ KitCgTypeKind kind; /* exact kind */
+ uint32_t flags; /* KitCgTypeFlag */
+ KitCgTypeLayout layout; /* valid only for sized/storage queries */
+} KitCgTypeInfo;
+
+KIT_API KitStatus kit_cg_type_info(KitCompiler*, KitCgTypeId,
+ KitCgTypeInfo*);
+```
+
+This descriptor is intentionally shallow. Shape-specific data still comes from
+shape-specific APIs or internal entry access:
+
+- pointer: pointee + address space
+- array: element + count
+- function: result/params/call convention
+- record: field count and field descriptors
+- enum: base and values
+- alias: name and base
+
+### 4.5 Record Declaration and Completion
+
+Records are true nominal objects.
+
+```c
+KIT_API KitCgTypeId kit_cg_type_record_decl(KitCompiler*, KitSym tag,
+ int is_union);
+
+KIT_API KitStatus kit_cg_type_record_complete(KitCompiler*, KitCgTypeId record,
+ const KitCgRecordDesc*);
+
+KIT_API int kit_cg_type_is_complete(KitCompiler*, KitCgTypeId);
+```
+
+Convenience construction stays possible, but it is just declare + complete:
+
+```c
+KIT_API KitCgTypeId kit_cg_type_record(KitCompiler*, const KitCgRecordDesc*);
+```
+
+Completion rules:
+
+- A record can be completed exactly once.
+- Field types must be valid.
+- A by-value field must be sized and complete.
+- A pointer field may point to an incomplete record.
+- A record cannot contain itself by value.
+- Layout caches for the record are invalid until completion succeeds.
+
+The C frontend no longer lowers record-context pointers to `void*`.
+
+### 4.6 Array Counts
+
+The public array count remains `uint64_t`. The structural key must use the full
+64-bit count or reject out-of-range counts with a documented status. Do not
+truncate the key. Prefer full-width support unless an object/backend limit
+requires a specific diagnostic.
+
+```c
+KIT_API KitCgTypeId kit_cg_type_array(KitCompiler*, KitCgTypeId elem,
+ uint64_t count);
+```
+
+---
+
+## 5. Internal Architecture
+
+### 5.1 Type Table
+
+Use one compiler-owned table. Builtins occupy the first nonzero ids; user entries
+append after them.
+
+```c
+typedef struct CgTypeEntry {
+ KitCgTypeKind kind;
+ uint32_t flags;
+
+ KitCgTypeId id;
+ KitCgTypeId storage_id; /* alias terminal once known */
+
+ uint8_t pred_bits;
+ uint8_t pred_valid;
+ uint8_t class_bits;
+ uint8_t class_valid;
+ uint8_t layout_valid;
+
+ KitCgTypeLayout layout;
+
+ union {
+ CgBuiltinType builtin;
+ CgPtrType ptr;
+ CgArrayType array;
+ CgFuncType func;
+ CgRecordType record;
+ CgEnumType enum_;
+ CgAliasType alias;
+ };
+} CgTypeEntry;
+
+typedef struct CgTypeTable {
+ Heap* heap;
+ CgTypeEntries entries;
+ CgPtrMap ptr_index;
+ CgArrayMap array_index;
+ CgFuncMap func_index;
+} CgTypeTable;
+```
+
+The table can still use a segmented vector if stable entry pointers are useful,
+but indexes are direct: `id - 1` maps to an entry. Hash indexes store ids, not
+entry pointers, so the implementation is free to move entries later if that
+becomes desirable.
+
+### 5.2 Structural Indexes
+
+Pointer key:
+
+```c
+{ pointee_id, address_space }
+```
+
+Array key:
+
+```c
+{ elem_id, uint64_t count }
+```
+
+Function key:
+
+```c
+{ result type+attrs, params type+attrs[], call_conv, abi_variadic }
+```
+
+The key is exact-id based. Alias preservation is therefore possible for source
+facing APIs and generated C/debug output. Storage equivalence is a separate
+query and must not depend on exact id equality.
+
+### 5.3 Layout Cache
+
+`CgTypeEntry.layout` is a cache of the ABI-computed layout, not an independent
+source of truth.
+
+Flow:
+
+```text
+kit_cg_type_size/align/info
+ -> cg_type_require_layout
+ -> TargetABI layout hook / shared ABI layout
+ -> cache on CgTypeEntry after success
+```
+
+Builtins can be initialized with target facts, but they still follow the same
+layout contract. Target-shaped builtins such as `vararg_state` are computed
+through the ABI layer.
+
+Record completion asks the ABI/layout authority to compute source-facing record
+layout before marking the record complete. If a future ABI has different
+bit-field layout rules, it plugs in at this boundary rather than after the
+record is already committed.
+
+### 5.4 Predicates and Classes
+
+Predicate bits and codegen class bits remain valuable, but they should be
+computed from one storage descriptor:
+
+```text
+exact id -> resolve alias -> storage/layout descriptor -> pred/class bits
+```
+
+That avoids one-off predicate functions each re-decoding the id and each
+choosing its own alias behavior.
+
+---
+
+## 6. Frontend Impact
+
+### 6.1 C Frontend
+
+The C frontend should map source types to CG types as follows:
+
+- C scalar type -> builtin storage id
+- C pointer type -> pointer to the exact lowered pointee id
+- C function type -> function id with a real void result type when appropriate
+- C record declaration -> `kit_cg_type_record_decl`
+- C record definition -> `kit_cg_type_record_complete`
+- C enum -> enum nominal id with integer base
+- C typedef -> source alias id only if CG/debug/C-backend output needs the
+ source-facing name; storage semantics must use alias resolution
+
+The current incomplete-record workaround is deleted. The C type pool can cache
+the declared record id as soon as the tag exists, then complete the same id when
+the definition is parsed.
+
+### 6.2 Toy Frontend
+
+Toy mostly uses CG storage types directly. It should switch to:
+
+- real void result type in function signatures
+- explicit storage/equivalence queries where it currently relies on exact
+ `kit_cg_type_kind`
+- record construction through the new declare/complete convenience API
+
+### 6.3 C Backend and Debug Info
+
+The C backend and debug producer are the main consumers that care about exact
+source-facing ids. They should use exact queries for names and aliases, and
+storage queries for codegen legality.
+
+---
+
+## 7. Implementation Plan
+
+### Phase 1: Lock the New Contract in Tests
+
+Add focused `test/api/cg_type_test.c` cases that fail under the old model:
+
+- void function result uses the void builtin, never `NONE`
+- invalid ids stay invalid and never masquerade as void
+- alias exact kind is `KIT_CG_TYPE_ALIAS`
+- alias storage info reports the base storage kind/layout
+- `kit_cg_type_same_storage` succeeds for alias-equivalent storage shapes
+- record declaration creates an incomplete nominal id
+- pointer to incomplete record is legal
+- array of incomplete record is rejected
+- record completion fills fields/layout and cannot run twice
+- recursive by-pointer records are legal
+- recursive by-value records are rejected
+- full-width array count keys do not collide
+
+### Phase 2: Replace the Type Table
+
+Introduce the new direct-index table behind `src/cg/type.c`:
+
+- builtins are entries `1..KIT_CG_BUILTIN_COUNT`
+- user entries append after builtins
+- `cg_type_get` becomes direct index validation
+- structural maps store ids
+- remove segment/bias decode helpers
+
+Do this before converting all callers, accepting compile failures in the
+working tree during the phase. There is no compatibility shim.
+
+### Phase 3: Cut Over Public API and Call Sites
+
+Update `include/kit/cg.h` and every caller:
+
+- replace `kit_cg_builtin_type_id` with `kit_cg_type_builtin`
+- replace `result.type == KIT_CG_TYPE_NONE` void checks with void-builtin checks
+- update scope/block APIs that currently use `NONE` for statement/no-result so
+ absence remains explicit and cannot be confused with the void type
+- replace ambiguous one-off queries with exact or storage queries
+- update function, intrinsic, call, local, memory, and data APIs to require real
+ type ids where a type is required
+
+### Phase 4: Move Layout Authority to ABI
+
+Make `TargetABI` the only layout authority:
+
+- scalar/builtin layout through ABI helpers
+- pointer layout through target spec read by ABI
+- array layout from element ABI layout
+- record layout during completion through an ABI record-layout hook
+- `kit_cg_type_size/align/info` read cached ABI layout, computing on demand
+
+Delete independent eager `CgType.size` / `CgType.align` ownership. A type entry
+may cache layout but does not define it.
+
+### Phase 5: Add Two-Phase Records and Convert C Lowering
+
+Convert the C frontend record path:
+
+- create/get CG record id at tag declaration time
+- complete that same id at record definition time
+- preserve pointer-to-incomplete-record identity
+- delete `TYPE_CG_RECORD_FIELD` pointee erasure to `void*`
+- delete incomplete-record temporary lowering to `void`
+- keep source diagnostics in the C frontend for illegal incomplete uses
+
+### Phase 6: Delete Old Caches and Helpers
+
+Remove now-redundant pieces:
+
+- segment id constants and decode helpers
+- recursive alias helper conventions in random query functions
+- `api_unalias_type` call-site scatter where replaced by descriptor queries
+- duplicated eager layout caches in ABI/CG if the entry layout cache covers the
+ use case
+- C frontend record memo workarounds that exist only because CG lacked
+ incomplete nominal records
+
+---
+
+## 8. Validation
+
+Use targeted runs during the cutover:
+
+- `make test-cg-api`
+- `make test-parse`
+- `make test-toy`
+- `make test-opt`
+- `make test-aa64-inline`
+- targeted smoke tests for one native 64-bit target and one non-aa64 backend
+ touched by layout or call classification
+
+Broaden only after the API and frontend are compiling cleanly.
+
+Important scenario coverage:
+
+- C recursive structs by pointer
+- incomplete record misuse diagnostics
+- function pointers involving records/enums/aliases
+- varargs and `va_list`
+- bit-fields, packed records, explicit/max alignment
+- enum integer bases
+- generated C backend output for aliases/records
+- debug info typedef/record naming
+
+---
+
+## 9. Acceptance Criteria
+
+- `KIT_CG_TYPE_NONE` means invalid/absent only; no function result or value type
+ uses it to mean void.
+- Builtin and user ids decode by direct table index, with no segment/bias scheme.
+- Pointer, array, and function interning remains O(1)-average and collision-safe.
+- Aliases have explicit exact-vs-storage semantics.
+- Records can be declared incomplete and completed once.
+- The C frontend no longer erases record-context pointer pointees to `void*`.
+- Size/align/scalar facts come from the ABI layout path.
+- Existing type hot paths still have cached predicate/class/layout bytes, but
+ those caches derive from the new descriptor model.
+- No global state, no VLAs, and all state hangs off `KitCompiler` or
+ `TargetABI`.
+
+---
+
+## 10. Open Questions
+
+1. Should `KIT_CG_TYPE_ALIAS` remain a true public kind, or should source aliases
+ move entirely into debug/C-backend metadata after the cutover? The plan above
+ keeps alias ids because they already serve source-facing consumers, but the
+ storage semantics do not require them.
+2. Should function types reject incomplete by-value records at construction, or
+ permit declaration-like signatures and reject only when defining/calling?
+ Prefer rejection unless a frontend has a concrete declaration use case.
+3. Should `kit_cg_type_info` include shallow shape facts, or stay layout-only
+ with shape-specific accessors? Prefer shallow layout-only initially to keep
+ the public struct stable.
+4. Should array counts larger than target addressable object size be rejected by
+ the type constructor or by object/data/local users? Prefer constructor
+ rejection if every backend agrees on the same maximum; otherwise diagnose at
+ the first sized-object use.
diff --git a/doc/plan/README.md b/doc/plan/README.md
@@ -11,6 +11,7 @@ shrinks to whatever remains open.
| [RELEASE.md](RELEASE.md) | Cross-cutting initial-release punchlist: release scope, deferred features, and per-subsystem completion/validation items. | — |
| [OPTIMIZER.md](OPTIMIZER.md) | Completing the O2 SSA mid-end, expanded inlining, -O0/-O1 performance work, machine register-constraint improvements. | [../OPT.md](../OPT.md) |
| [PERF.md](PERF.md) | Making kit the fastest `-O0` C compiler with code as dense as tcc: current compile-speed + code-size standings, how to reproduce them (macOS instruction counts, Linux callgrind, the `make bench-cc` scaling guard), and the ranked forward-looking levers on both axes. | [../ARCH.md](../ARCH.md) |
+| [CG-TYPES.md](CG-TYPES.md) | Clean-break redesign of the public CG type/type-id system: direct-index handles, real void types, explicit alias/storage semantics, ABI-owned layout, and two-phase nominal records. | [../CODEGEN.md](../CODEGEN.md), [../INTERFACES.md](../INTERFACES.md) |
| [CG-STACK-API.md](CG-STACK-API.md) | Refactoring the parser-to-CG expression seam: keep stack-based liveness, move the C frontend's duplicated `pcg` slot state onto CG-owned stack slots, and add parser-shaped place/load/store operations without backend fusion. | [../FRONTENDS.md](../FRONTENDS.md) |
| [LEX-PP-API.md](LEX-PP-API.md) | Redesigning the lexer -> preprocessor -> parser boundary around lean tokens, lazy spelling/location materialization, and slot-based handoffs for a high-performance lexer/preprocessor rewrite without frontend layer fusion. | [../FRONTENDS.md](../FRONTENDS.md) |
| [LINKER.md](LINKER.md) | Incremental linking: the file-based object-link redesign and remaining non-ELF format coverage. | [../LINK.md](../LINK.md) |
diff --git a/include/kit/cg.h b/include/kit/cg.h
@@ -20,6 +20,8 @@ typedef uint32_t KitCgTypeId;
#define KIT_CG_LOCAL_NONE 0u
#define KIT_CG_SCOPE_NONE 0u
#define KIT_CG_SYM_NONE 0u
+/* Invalid or absent type sentinel. Void is represented by the real void
+ * builtin type id, never by KIT_CG_TYPE_NONE. */
#define KIT_CG_TYPE_NONE 0u
/* ============================================================
@@ -41,10 +43,6 @@ typedef enum KitCgBuiltinType {
KIT_CG_BUILTIN_COUNT,
} KitCgBuiltinType;
-typedef struct KitCgBuiltinTypes {
- KitCgTypeId id[KIT_CG_BUILTIN_COUNT];
-} KitCgBuiltinTypes;
-
typedef enum KitCgTypeKind {
KIT_CG_TYPE_VOID,
KIT_CG_TYPE_BOOL,
@@ -98,15 +96,16 @@ typedef struct KitCgFuncParam {
KitCgAbiAttrs attrs;
} KitCgFuncParam;
-/* Symmetric with KitCgFuncParam. A function returns a single value or nothing;
- * result.type == KIT_CG_TYPE_NONE means void. */
+/* Symmetric with KitCgFuncParam. result.type is always a valid type id; use
+ * kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID) for a function that returns no
+ * value. */
typedef struct KitCgFuncResult {
KitCgTypeId type;
KitCgAbiAttrs attrs;
} KitCgFuncResult;
typedef struct KitCgFuncSig {
- KitCgFuncResult result; /* result.type == KIT_CG_TYPE_NONE == void */
+ KitCgFuncResult result; /* void builtin means no value */
const KitCgFuncParam* params;
uint32_t nparams;
KitCgCallConv call_conv;
@@ -143,39 +142,99 @@ typedef struct KitCgRecordDesc {
uint32_t align_override; /* 0 = natural, >0 explicit record alignment */
} KitCgRecordDesc;
-/* Builtin ids are stable for the compiler. Pointer, array, and function
- * constructors return a stable id for the same shape within one compiler;
- * aliases, records, and enums allocate fresh user-facing identities.
+typedef enum KitCgTypeFlag {
+ KIT_CG_TYPEF_COMPLETE = 1u << 0,
+ KIT_CG_TYPEF_SIZED = 1u << 1,
+ KIT_CG_TYPEF_BUILTIN = 1u << 2,
+ KIT_CG_TYPEF_NOMINAL = 1u << 3,
+} KitCgTypeFlag;
+
+typedef enum KitCgStorageKind {
+ KIT_CG_STORAGE_VOID,
+ KIT_CG_STORAGE_BOOL,
+ KIT_CG_STORAGE_INT,
+ KIT_CG_STORAGE_FLOAT,
+ KIT_CG_STORAGE_PTR,
+ KIT_CG_STORAGE_AGGREGATE,
+} KitCgStorageKind;
+
+typedef struct KitCgTypeLayout {
+ uint64_t size;
+ uint32_t align;
+ uint16_t scalar_width; /* bit width for scalar int/float/bool, else 0 */
+ uint8_t storage_kind; /* KitCgStorageKind */
+ uint8_t valid; /* 0 for invalid, incomplete, or unsized types */
+} KitCgTypeLayout;
+
+typedef struct KitCgTypeInfo {
+ KitCgTypeId id;
+ KitCgTypeId storage_id; /* alias-resolved terminal id, or id */
+ KitCgTypeKind kind; /* exact kind */
+ uint32_t flags; /* KitCgTypeFlag */
+ KitCgTypeLayout layout; /* ABI/storage layout; valid only when sized */
+} KitCgTypeInfo;
+
+/* Type ids are stable only within one compiler. Pointer, array, and function
+ * constructors return a stable id for the same exact shape within one compiler;
+ * aliases, records, and enums allocate fresh user-facing identities. Void is a
+ * builtin type id; KIT_CG_TYPE_NONE is invalid/absent only.
*
* Integer types are width-only storage types. Signedness is carried by
* operations, comparisons, conversions, and ABI extension attributes. */
-KIT_API KitCgBuiltinTypes kit_cg_builtin_types(KitCompiler*);
-
-/* Single builtin id. Equivalent to kit_cg_builtin_types(c).id[which] but
- * without materializing (and copying by value) the whole table -- the hot
- * type-lowering path queries one builtin at a time. */
-KIT_API KitCgTypeId kit_cg_builtin_type_id(KitCompiler*, KitCgBuiltinType which);
+KIT_API KitCgTypeId kit_cg_type_builtin(KitCompiler*, KitCgBuiltinType which);
/* Interned structural types. Address space 0 is the normal target data
- * address space. */
+ * address space. Function signatures must use the void builtin type id as the
+ * result type for a no-value return. */
KIT_API KitCgTypeId kit_cg_type_func(KitCompiler*, KitCgFuncSig sig);
KIT_API KitCgTypeId kit_cg_type_ptr(KitCompiler*, KitCgTypeId pointee,
uint32_t address_space);
KIT_API KitCgTypeId kit_cg_type_array(KitCompiler*, KitCgTypeId elem,
uint64_t count);
-/* Fresh nominal/source-facing types. Enums use a width-only integer base. */
-KIT_API KitCgTypeId kit_cg_type_alias(KitCompiler*, KitSym name,
- KitCgTypeId base);
-KIT_API KitCgTypeId kit_cg_type_record(KitCompiler*, KitSym tag,
- const KitCgField* fields,
- uint32_t nfields);
-KIT_API KitCgTypeId kit_cg_type_record_ex(KitCompiler*, const KitCgRecordDesc*);
+/* Enums use a width-only integer base. */
KIT_API KitCgTypeId kit_cg_type_enum(KitCompiler*, KitSym tag, KitCgTypeId base,
const KitCgEnumValue* values,
uint32_t nvalues);
-/* Type queries. These report codegen storage, ABI, and target layout facts. */
+/* Records are nominal and may be declared before they are complete. Pointers
+ * to incomplete records are valid; sizing, arrays, fields, by-value
+ * params/results, locals, and memory accesses require complete/sized types.
+ * Completion is one-shot and computes ABI/source layout for the record. */
+KIT_API KitCgTypeId kit_cg_type_record_decl(KitCompiler*, KitSym tag,
+ int is_union);
+KIT_API KitStatus kit_cg_type_record_complete(KitCompiler*, KitCgTypeId record,
+ const KitCgRecordDesc*);
+/* Convenience constructor: declare a fresh nominal record, complete it, and
+ * return its id, or KIT_CG_TYPE_NONE on failure. */
+static inline KitCgTypeId kit_cg_type_record(KitCompiler* c,
+ const KitCgRecordDesc* desc) {
+ KitCgTypeId record;
+ if (!desc) return KIT_CG_TYPE_NONE;
+ record = kit_cg_type_record_decl(c, desc->tag, desc->is_union);
+ if (record == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
+ if (kit_cg_type_record_complete(c, record, desc) != KIT_OK)
+ return KIT_CG_TYPE_NONE;
+ return record;
+}
+
+/* Fresh nominal/source-facing types. */
+KIT_API KitCgTypeId kit_cg_type_alias(KitCompiler*, KitSym name,
+ KitCgTypeId base);
+
+/* Type queries.
+ *
+ * kit_cg_type_kind is exact: alias ids report KIT_CG_TYPE_ALIAS. Layout and
+ * width queries report ABI/storage facts and resolve aliases. Shape-specific
+ * queries are exact: call kit_cg_type_resolve_alias first when storage shape is
+ * desired. */
+KIT_API KitStatus kit_cg_type_info(KitCompiler*, KitCgTypeId, KitCgTypeInfo*);
+KIT_API KitCgTypeId kit_cg_type_resolve_alias(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_type_same_storage(KitCompiler*, KitCgTypeId, KitCgTypeId);
+KIT_API int kit_cg_type_is_void(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_type_is_complete(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_type_is_sized(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_func_result_has_value(KitCompiler*, KitCgFuncResult);
KIT_API KitCgTypeKind kit_cg_type_kind(KitCompiler*, KitCgTypeId);
KIT_API uint64_t kit_cg_type_size(KitCompiler*, KitCgTypeId);
KIT_API uint32_t kit_cg_type_align(KitCompiler*, KitCgTypeId);
@@ -187,8 +246,7 @@ KIT_API uint32_t kit_cg_type_ptr_address_space(KitCompiler*, KitCgTypeId);
KIT_API KitCgTypeId kit_cg_type_array_elem(KitCompiler*, KitCgTypeId);
KIT_API uint64_t kit_cg_type_array_count(KitCompiler*, KitCgTypeId);
-/* The function's result; result.type == KIT_CG_TYPE_NONE for a void function.
- */
+/* The function's result. A no-value result uses the void builtin type id. */
KIT_API KitCgFuncResult kit_cg_type_func_result(KitCompiler*, KitCgTypeId);
KIT_API uint32_t kit_cg_type_func_nparams(KitCompiler*, KitCgTypeId);
KIT_API KitCgFuncParam kit_cg_type_func_param(KitCompiler*, KitCgTypeId,
@@ -196,12 +254,23 @@ KIT_API KitCgFuncParam kit_cg_type_func_param(KitCompiler*, KitCgTypeId,
KIT_API KitCgCallConv kit_cg_type_func_call_conv(KitCompiler*, KitCgTypeId);
KIT_API int kit_cg_type_func_is_variadic(KitCompiler*, KitCgTypeId);
+KIT_API KitSym kit_cg_type_record_tag(KitCompiler*, KitCgTypeId);
+KIT_API int kit_cg_type_record_is_union(KitCompiler*, KitCgTypeId);
KIT_API uint32_t kit_cg_type_record_nfields(KitCompiler*, KitCgTypeId);
/* Returns KIT_OK and fills any non-NULL out parameters on success. */
KIT_API KitStatus kit_cg_type_record_field(KitCompiler*, KitCgTypeId,
uint32_t index, KitCgField* out,
uint64_t* offset_out);
+KIT_API KitSym kit_cg_type_enum_tag(KitCompiler*, KitCgTypeId);
+KIT_API KitCgTypeId kit_cg_type_enum_base(KitCompiler*, KitCgTypeId);
+KIT_API uint32_t kit_cg_type_enum_nvalues(KitCompiler*, KitCgTypeId);
+KIT_API KitStatus kit_cg_type_enum_value(KitCompiler*, KitCgTypeId,
+ uint32_t index, KitCgEnumValue* out);
+
+KIT_API KitSym kit_cg_type_alias_name(KitCompiler*, KitCgTypeId);
+KIT_API KitCgTypeId kit_cg_type_alias_base(KitCompiler*, KitCgTypeId);
+
typedef enum KitCgSymbolFeature {
KIT_CG_SYMFEAT_WEAK,
KIT_CG_SYMFEAT_PROTECTED_VISIBILITY,
@@ -295,12 +364,16 @@ typedef enum KitCgMemAccessFlag {
/* Access is an externally observable side effect and must not be merged,
* removed, or reordered across other volatile accesses. */
KIT_CG_MEM_VOLATILE = 1u << 0,
- /* Codegen hint (loads only): the loaded value is a signed narrow integer that
- * the frontend will only ever widen by sign-extension, so the backend is free
- * to emit a single sign-extending load and the -O0 path may then elide the
- * following sign-extend convert. A backend that does not exploit it just emits
- * a plain load; semantics are unchanged. Ignored on stores and bit-fields. */
+ /* Backend hint (loads only): emit a sign-extending narrow load when
+ * supported. Frontends normally set KIT_CG_MEM_SOURCE_SIGNED and let
+ * kit_cg_load derive this for byte/half integer loads; direct callers may
+ * still set it when they already know the following use is sign-extension.
+ * Ignored on stores and bit-fields. */
KIT_CG_MEM_SEXT_LOAD = 1u << 1,
+ /* The source-language object being accessed is a signed integer. KitCgTypeId
+ * intentionally carries width/category, not C signedness, so this keeps the
+ * source semantic fact separate from the backend SEXT-load hint above. */
+ KIT_CG_MEM_SOURCE_SIGNED = 1u << 2,
} KitCgMemAccessFlag;
typedef struct KitCgMemAccess {
@@ -541,11 +614,11 @@ KIT_API void kit_cg_alloca(KitCg*, uint32_t align, KitCgTypeId result_ptr_type);
/* Structured control flow scopes.
*
- * result_type determines whether the scope carries a value:
- * KIT_CG_TYPE_NONE - statement scope, no result.
- * otherwise - expression scope; break carries a result.
+ * kit_cg_scope_begin / kit_cg_block_begin create statement scopes with no
+ * result values. The *_value variants create single-result expression scopes;
+ * result_type must be a valid non-void type id.
*
- * Stack effects when result_type != NONE:
+ * Stack effects for a value scope:
* break(scope) -> pop result, exit scope
* break_true(scope) -> stack is [result, bool]; pop bool; if true, pop
* result and exit; otherwise pop result
@@ -554,14 +627,16 @@ KIT_API void kit_cg_alloca(KitCg*, uint32_t align, KitCgTypeId result_ptr_type);
*
* continue/continue_true/continue_false never carry a result; they jump to
* the loop header, not the scope exit. */
-KIT_API KitCgScope kit_cg_scope_begin(KitCg*, KitCgTypeId result_type);
+KIT_API KitCgScope kit_cg_scope_begin(KitCg*);
+KIT_API KitCgScope kit_cg_scope_begin_value(KitCg*, KitCgTypeId result_type);
/* Like kit_cg_scope_begin but opens a forward-only block: break exits past
* the scope; continue is not legal. This is the natural shape for `if`/
* `if-else` (a wrapping block whose break is "skip the rest"), and lets
* backends with structured control flow (Wasm) emit `block`/`end` directly
* instead of recognizing arbitrary forward-only label-and-jump patterns. */
-KIT_API KitCgScope kit_cg_block_begin(KitCg*, KitCgTypeId result_type);
+KIT_API KitCgScope kit_cg_block_begin(KitCg*);
+KIT_API KitCgScope kit_cg_block_begin_value(KitCg*, KitCgTypeId result_type);
/* Multi-value scopes: a block/loop that carries N params and/or N results
* (the value-stack analogue of a Wasm multi-value block). params/results are
@@ -575,16 +650,18 @@ KIT_API KitCgScope kit_cg_block_begin(KitCg*, KitCgTypeId result_type);
* kit_cg_scope_store_params (which pops `nparams` into those locals, no
* jump) followed by a jump to kit_cg_scope_continue_label.
*
- * kit_cg_scope_begin / kit_cg_block_begin are the nparams==0, nresults<=1
- * special cases and emit an identical op sequence. break, break_true,
- * break_false, the continue ops and scope_end all generalize to N values. */
+ * kit_cg_scope_begin / kit_cg_block_begin are the nparams==0, nresults==0
+ * special cases; the *_value variants are nparams==0, nresults==1.
+ * Results must be valid non-void type ids. break, break_true, break_false, the
+ * continue ops and scope_end all generalize to N values. */
typedef struct KitCgScopeSig {
const KitCgTypeId* params; /* value-stack bottom -> top */
uint32_t nparams;
const KitCgTypeId* results; /* value-stack bottom -> top */
uint32_t nresults;
} KitCgScopeSig;
-KIT_API KitCgScope kit_cg_scope_begin_sig(KitCg*, const KitCgScopeSig*); /*loop*/
+KIT_API KitCgScope kit_cg_scope_begin_sig(KitCg*,
+ const KitCgScopeSig*); /*loop*/
KIT_API KitCgScope kit_cg_block_begin_sig(KitCg*, const KitCgScopeSig*);
/* Pop `nparams` values into the loop's param locals (bottom->top), without
* jumping. The caller then jumps kit_cg_scope_continue_label to take the back
@@ -700,13 +777,57 @@ KIT_API void kit_cg_dup2(KitCg*); /* duplicates the top two slots */
KIT_API void kit_cg_swap(KitCg*);
KIT_API void kit_cg_drop(KitCg*);
KIT_API void kit_cg_rot3(KitCg*); /* [..., a, b, c] -> [..., b, c, a] */
+
+typedef struct KitCgSlotInfo {
+ KitCgTypeId cg_type;
+ const void* lang_type;
+ uint16_t lang_flags;
+} KitCgSlotInfo;
+
+/* Inspect and update frontend-owned facts on value-stack slots. CG treats
+ * lang_type as opaque and lang_flags as frontend-defined bits; stack-producing
+ * operations clear them, while structural stack operations copy/move them with
+ * the slot. depth_from_top 0 names TOS. */
+KIT_API KitCgSlotInfo kit_cg_slot_info(KitCg*, uint32_t depth_from_top);
+KIT_API void kit_cg_retag_top(KitCg*, const void* lang_type,
+ uint16_t lang_flags);
+KIT_API void kit_cg_retag_at(KitCg*, uint32_t depth_from_top,
+ const void* lang_type, uint16_t lang_flags);
+KIT_API void kit_cg_set_top_flags(KitCg*, uint16_t set, uint16_t clear);
+
/* The current value-stack depth. Lets a frontend record a base depth and later
* drop back to it (e.g. discarding the operands a Wasm polymorphic/unreachable
* region left behind). */
KIT_API uint32_t kit_cg_stack_depth(KitCg*);
-/* Returns nonzero when the current top-of-stack value is a compile-time known
- * integer-like immediate VALUE. The value is not popped. */
+typedef struct KitCgConstInt {
+ uint64_t lo; /* low bits, always truncated to width */
+ uint64_t hi; /* high bits for width > 64 */
+ uint16_t width; /* 1..128 for integer constants */
+ uint8_t is_signed; /* interpretation hint for convenience queries */
+ uint8_t known; /* 0 => not a compile-time integer constant */
+} KitCgConstInt;
+
+/* Suppress target emission while preserving value-stack shape and stack-owned
+ * constant facts. Nested pushes are allowed; pop diagnoses underflow. */
+KIT_API void kit_cg_unevaluated_push(KitCg*);
+KIT_API void kit_cg_unevaluated_pop(KitCg*);
+
+/* Width-complete integer-constant query. Reads the stack-side constant payload,
+ * not the emitted operand shape, and does not pop the value. */
+KIT_API int kit_cg_top_const_int_ex(KitCg*, KitCgConstInt* out_value);
+
+/* Convenience query for constants representable as one 64-bit lane. */
+KIT_API int kit_cg_top_const_i64(KitCg*, int64_t* out_value);
+
+/* Push a scalar integer VALUE with an explicit width-complete constant payload.
+ * The value is truncated to the destination type width when that width is
+ * integer-like and known. */
+KIT_API void kit_cg_push_const_int(KitCg*, KitCgTypeId type,
+ const KitCgConstInt* value);
+
+/* Compatibility wrapper for older callers that only need a signed 64-bit
+ * result. */
KIT_API int kit_cg_top_const_int(KitCg*, int64_t* out_value);
/* Push a scalar VALUE. */
@@ -761,6 +882,14 @@ KIT_API void kit_cg_deref(KitCg*, int64_t offset);
* the frontend rejects `&` / `sizeof` on a bit-field before reaching CG. */
KIT_API void kit_cg_field(KitCg*, uint32_t field_index);
+/* PLACE -> PLACE: project to a byte-offset subobject with a frontend-supplied
+ * field type. This is the layout-known counterpart to kit_cg_field: CG does no
+ * record lookup, but still folds byte_offset into the place operand when the
+ * target addressing mode can carry it. Bit-field geometry, if any, is attached
+ * separately with kit_cg_field_bits. */
+KIT_API void kit_cg_field_at(KitCg*, int64_t byte_offset,
+ KitCgTypeId field_type);
+
/* PLACE -> PLACE(bit-field): tag the TOS place as a bit-field place carrying
* the given bit geometry. This is the manual-addressing counterpart to the
* implicit bit-field projection `field` performs: a frontend that builds the
@@ -802,9 +931,11 @@ KIT_API void kit_cg_elem_scaled(KitCg*, uint32_t elem_size, int64_t offset);
*
* Stack effects:
* load: [place] -> [value]
- * store: [place, value] -> [] */
+ * store: [place, value] -> []
+ * store_keep: [place, value] -> [value] */
KIT_API void kit_cg_load(KitCg*, KitCgMemAccess access);
KIT_API void kit_cg_store(KitCg*, KitCgMemAccess access);
+KIT_API void kit_cg_store_keep(KitCg*, KitCgMemAccess access);
/* ============================================================
* ABI variadic argument access
@@ -1092,12 +1223,12 @@ typedef enum KitCgBarrierScope {
KIT_CG_BARRIER_NON_SHARE,
} KitCgBarrierScope;
-/* Pops nargs operands. Pushes result_type unless result_type is
- * KIT_CG_TYPE_NONE or void. Overflow intrinsics push two values:
- * result, overflow_bool regardless of result_type. Syscall uses nargs = argc +
- * 1: the syscall number plus 0..6 long arguments. Runtime-extension intrinsics
- * mirror rt/include/kit/{syscall,baremetal,coro}.h and must diagnose targets
- * where the primitive has no legal lowering. */
+/* Pops nargs operands. result_type must be a valid type id; the void builtin
+ * means no result is pushed. Overflow intrinsics push two values: result,
+ * overflow_bool regardless of result_type. Syscall uses nargs = argc + 1: the
+ * syscall number plus 0..6 long arguments. Runtime-extension intrinsics mirror
+ * rt/include/kit/{syscall,baremetal,coro}.h and must diagnose targets where
+ * the primitive has no legal lowering. */
KIT_API void kit_cg_intrinsic(KitCg*, KitCgIntrinsic, uint32_t nargs,
KitCgTypeId result_type);
@@ -1424,8 +1555,8 @@ typedef struct KitCgIf {
static inline KitCgIf kit_cg_if_begin(KitCg* cg) {
KitCgIf it;
- it.outer = kit_cg_block_begin(cg, KIT_CG_TYPE_NONE);
- it.inner = kit_cg_block_begin(cg, KIT_CG_TYPE_NONE);
+ it.outer = kit_cg_block_begin(cg);
+ it.inner = kit_cg_block_begin(cg);
kit_cg_break_false(cg, it.inner);
return it;
}
diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c
@@ -66,6 +66,8 @@ static KitCgMemAccess pcg_mem_id(Parser* p, KitCgTypeId id, const Type* ty) {
m.type = id;
m.align = (u32)kit_cg_type_align(p->c, id);
if (ty && (ty->qual & Q_VOLATILE)) m.flags |= KIT_CG_MEM_VOLATILE;
+ if (type_is_int(ty) && pcg_type_is_signed(ty))
+ m.flags |= KIT_CG_MEM_SOURCE_SIGNED;
return m;
}
@@ -123,7 +125,8 @@ void pcg_dup_type(Parser* p) {
pcg_aux_clear(&top.aux);
}
/* Copy the slot out before push: pcg_stack_grow may reallocate. The whole
- * slot (cg_id included) is copied back at the end, so dup preserves the id. */
+ * slot (cg_id included) is copied back at the end, so dup preserves the id.
+ */
pcg_push_type(p, top.type);
if (p->cg_type_sp) p->cg_slot_stack[p->cg_type_sp - 1u] = top;
}
@@ -166,8 +169,9 @@ void pcg_drop(Parser* p) {
/* Reclaim dead compiler-temp slots at a statement boundary. The authoritative
* "no value is live" witness is the CG value stack depth, which
* kit_cg_reclaim_temps checks (g->sp == 0): a stray non-empty call is a safe
- * no-op rather than a miscompile. (The parser's typed shadow stack cg_type_sp is
- * NOT a reliable witness — it is not drained in lockstep with the value stack.) */
+ * no-op rather than a miscompile. (The parser's typed shadow stack cg_type_sp
+ * is NOT a reliable witness — it is not drained in lockstep with the value
+ * stack.) */
void pcg_reclaim_temps(Parser* p) {
if (!pcg_emit_enabled(p)) return;
kit_cg_reclaim_temps(p->cg);
@@ -532,15 +536,6 @@ void pcg_load(Parser* p) {
KitCgMemAccess access = pcg_mem_id(p, cg_id, ty);
/* Snapshot bit-field geometry before materialize clears the aux. */
PcgLvAux bf = lv ? *lv : (PcgLvAux){0};
- /* Widening signed load (Lever 4): a non-bit-field signed byte/short load is
- * only ever widened by sign-extension (the integer-promotion CV_SEXT), so
- * mark the access so the backend can fold the load + sign-extend into one
- * ldrsb/ldrsh and the -O0 path can drop the redundant CV_SEXT. Plain hint;
- * a backend that ignores it stays correct. */
- if (bf.bit_width == 0 && type_is_int(ty) && pcg_type_is_signed(ty) &&
- (u32)kit_cg_type_size(p->c, cg_id) < 4u) {
- access.flags |= KIT_CG_MEM_SEXT_LOAD;
- }
/* Build the PLACE the strict load requires. A trivial local already has its
* PLACE on the CG stack (push_local) and loads directly; anything else is
* reduced to a single pointer (materialize, or a pointer-rvalue base) and
@@ -637,9 +632,9 @@ static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty) {
/* Build the PLACE a load/store reads, from the TOS lvalue, keeping a constant
* field/element offset as the deref displacement (so the backend folds it into
* the load/store: `ldr w, [base, #ofs]`) rather than baking it into an explicit
- * `base + ofs` pointer the way pcg_materialize_lv_to_ptr + kit_cg_deref(0) does.
- * Mirrors that pair's postcondition (CG TOS is the place; parser slot is the
- * field pointer type with aux cleared). The pointer is reinterpreted to the
+ * `base + ofs` pointer the way pcg_materialize_lv_to_ptr + kit_cg_deref(0)
+ * does. Mirrors that pair's postcondition (CG TOS is the place; parser slot is
+ * the field pointer type with aux cleared). The pointer is reinterpreted to the
* field type with a bitcast — free on the native backend (a no-op same-width
* cast), an explicit cast on the C-source backend. Indexed (scale != 0) and
* out-of-displacement-range offsets fall back to the explicit-pointer fold.
@@ -666,11 +661,12 @@ static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty) {
* pointer value, then fuse. The index stride is the containing array's
* element size (scale) while the access type is field_ty, so `a[i].f`
* strides by sizeof(elem) but reads `f`. */
- kit_cg_swap(p->cg); /* [index, base] */
- if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg); /* [index, base_ptr] */
- kit_cg_bitcast(p->cg, pcg_tid(p, fptr)); /* [index, (T*)base] */
- kit_cg_swap(p->cg); /* [base, index] */
- kit_cg_elem_scaled(p->cg, scale, ofs); /* [place] */
+ kit_cg_swap(p->cg); /* [index, base] */
+ if (base_kind == PCG_LV_BASE_LOCAL)
+ kit_cg_addr(p->cg); /* [index, base_ptr] */
+ kit_cg_bitcast(p->cg, pcg_tid(p, fptr)); /* [index, (T*)base] */
+ kit_cg_swap(p->cg); /* [base, index] */
+ kit_cg_elem_scaled(p->cg, scale, ofs); /* [place] */
} else {
if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg);
kit_cg_bitcast(p->cg, pcg_tid(p, fptr));
@@ -743,7 +739,8 @@ static void pcg_store_impl(Parser* p, int keep_result) {
const Type* lv_ty = pcg_top2_type(p);
const Type* rv_ty = pcg_top_type(p);
const Type* mem_ty = lv_ty;
- /* Cached CG ids of the two slots (lowered once, not re-crossed by pcg_mem). */
+ /* Cached CG ids of the two slots (lowered once, not re-crossed by pcg_mem).
+ */
KitCgTypeId rv_cg = pcg_top_cg_id(p);
KitCgTypeId mem_cg = pcg_top2_cg_id(p); /* tracks mem_ty (= lv_ty) */
int emit = pcg_emit_enabled(p);
@@ -1278,7 +1275,8 @@ void pcg_intrinsic_void(Parser* p, IntrinKind k) {
if (pcg_emit_enabled(p)) kit_cg_unreachable(p->cg);
} else {
if (pcg_emit_enabled(p)) {
- kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_TRAP, 0, KIT_CG_TYPE_NONE);
+ kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_TRAP, 0,
+ kit_cg_type_builtin(p->c, KIT_CG_BUILTIN_VOID));
}
}
}
diff --git a/lang/c/parse/parse_stmt.c b/lang/c/parse/parse_stmt.c
@@ -86,7 +86,7 @@ static void parse_while_stmt(Parser* p) {
CGLabel L_end;
int emit = pcg_emit_enabled(p);
if (emit) {
- scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
+ scope = kit_cg_scope_begin(p->cg);
L_top = kit_cg_scope_continue_label(p->cg, scope);
L_end = kit_cg_scope_break_label(p->cg, scope);
} else {
@@ -416,7 +416,7 @@ static void parse_switch_stmt(Parser* p) {
CaseEntry* head;
if (emit) {
- scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
+ scope = kit_cg_scope_begin(p->cg);
L_dispatch = pcg_label_new(p);
L_end = kit_cg_scope_break_label(p->cg, scope);
} else {
diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c
@@ -1499,6 +1499,24 @@ const Type* apply_decl_suffix(Parser* p, const Type* base,
if (base && base->kind == TY_VOID) {
perr(p, "array of void type is invalid");
}
+ /* C11 6.7.6.2p1: an array's element type must be complete. Completeness is
+ * fixed where the array declarator is formed, so an incomplete record stays
+ * an error here even if its tag is completed later (or never) -- this is
+ * the constraint that rejects `struct N { struct N a[10]; }` while a
+ * pointer element (`struct N *a[10]`) stays legal because a pointer is
+ * complete. Element incompleteness is checked only for records: VLAs share
+ * the array `incomplete` flag, so an incomplete-array element cannot be
+ * told apart from a valid VLA element here and is intentionally not
+ * diagnosed. */
+ if (base && (base->kind == TY_STRUCT || base->kind == TY_UNION) &&
+ base->rec.incomplete) {
+ if (base->rec.tag) {
+ perr(p, "array has incomplete element type '%s %.*s'",
+ base->kind == TY_UNION ? "union" : "struct",
+ KIT_SLICE_ARG(kit_sym_str(p->pool->c, base->rec.tag)));
+ }
+ perr(p, "array has incomplete element type");
+ }
return type_array(p->pool, base, s->count, s->incomplete || s->vla);
}
{
diff --git a/lang/c/type/type.c b/lang/c/type/type.c
@@ -97,7 +97,8 @@ static int type_struct_eq(const Type* a, const Type* b) {
* declaration identity on `derived` and never enter this set. Replaces the
* former O(types) linear scan of one flat derived list (which made each
* derivation O(types) → type construction O(types^2)). */
-KIT_HASHSET_DEFINE(TypeInternSet, const Type*, type_struct_hash, type_struct_eq);
+KIT_HASHSET_DEFINE(TypeInternSet, const Type*, type_struct_hash,
+ type_struct_eq);
/* Completed record layout id, keyed by record identity. A record's identity is
* its tag id (same_record_type is tag-id equality), so a plain u32->id map
@@ -113,9 +114,10 @@ static inline u32 type_ptr_hash(const Type* t) {
/* Canonical-unqualified Type* memo, keyed by the qualified Type*. Collapses the
* O(derived) linear scan type_unqual runs for a complete tagged type (the hot
- * const-record path) to one lookup. Only stable answers are inserted (a complete
- * record's unqualified node never changes); incomplete/enum results are left
- * uncached because they can still change as a forward record completes. */
+ * const-record path) to one lookup. Only stable answers are inserted (a
+ * complete record's unqualified node never changes); incomplete/enum results
+ * are left uncached because they can still change as a forward record
+ * completes. */
KIT_HASHMAP_DEFINE(CgUnqualMemo, const Type*, const Type*, type_ptr_hash);
typedef struct PoolTypeCache {
@@ -129,6 +131,12 @@ typedef struct PoolTypeCache {
TypeListNode* derived;
/* Completed record layout ids, keyed by tag id. */
CgRecordMap cg_records;
+ /* Records whose CG completion is currently in flight, keyed by tag id ->
+ * decl id. Self-/mutually-recursive records reach themselves through a
+ * pointer field mid-completion; this lets type_cg_record_layout hand back the
+ * still-incomplete decl id (a pointer to an incomplete record is legal)
+ * instead of recursing into a second completion of the same record. */
+ CgRecordMap cg_records_completing;
/* Canonical-unqualified node per qualified Type* (stable answers only). */
CgUnqualMemo unqual;
/* Tag id allocator (1-based; TAG_NONE = 0). */
@@ -147,6 +155,7 @@ static PoolTypeCache* cache_get(Pool* p) {
* needed. Lazy (cap 0): the first insert allocates. */
TypeInternSet_init_cap(&c->structural, &p->arena_heap, 0);
CgRecordMap_init_cap(&c->cg_records, &p->arena_heap, 0);
+ CgRecordMap_init_cap(&c->cg_records_completing, &p->arena_heap, 0);
CgUnqualMemo_init_cap(&c->unqual, &p->arena_heap, 0);
p->type_cache = c;
return c;
@@ -292,8 +301,8 @@ static int type_tagged_eq(const Type* n, const Type* base, u16 qual) {
n->rec.max_align == base->rec.max_align &&
n->rec.align_override == base->rec.align_override;
case TY_ENUM:
- return n->enm.tag_id == base->enm.tag_id &&
- n->enm.tag == base->enm.tag && n->enm.base == base->enm.base;
+ return n->enm.tag_id == base->enm.tag_id && n->enm.tag == base->enm.tag &&
+ n->enm.base == base->enm.base;
default:
return 0;
}
@@ -691,40 +700,40 @@ static KitCgTypeId type_cg_builtin(KitCompiler* c, TypeKind kind) {
* widths, so it is fetched lazily inside those cases rather than per call. */
switch (kind) {
case TY_VOID:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_VOID);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
case TY_BOOL:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_BOOL);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_BOOL);
case TY_CHAR:
case TY_SCHAR:
case TY_UCHAR:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_I8);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_I8);
case TY_SHORT:
case TY_USHORT:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_I16);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_I16);
case TY_INT:
case TY_UINT:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_I32);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
case TY_LONG:
case TY_ULONG:
- return kit_cg_builtin_type_id(
+ return kit_cg_type_builtin(
c, kit_target_uses_lp64(kit_compiler_target_spec(c))
? KIT_CG_BUILTIN_I64
: KIT_CG_BUILTIN_I32);
case TY_LLONG:
case TY_ULLONG:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_I64);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
case TY_INT128:
case TY_UINT128:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_I128);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_I128);
case TY_FLOAT:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_F32);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_F32);
case TY_DOUBLE:
- return kit_cg_builtin_type_id(c, KIT_CG_BUILTIN_F64);
+ return kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64);
case TY_LDOUBLE:
/* `long double` is IEEE-754 binary128 on targets that follow the quad
* psABI (RISC-V, aarch64-linux, wasm32); elsewhere it aliases `double`.
* See kit_target_long_double_is_binary128. */
- return kit_cg_builtin_type_id(
+ return kit_cg_type_builtin(
c, kit_target_long_double_is_binary128(kit_compiler_target_spec(c))
? KIT_CG_BUILTIN_F128
: KIT_CG_BUILTIN_F64);
@@ -736,7 +745,6 @@ static KitCgTypeId type_cg_builtin(KitCompiler* c, TypeKind kind) {
typedef enum TypeCgMode {
TYPE_CG_VALUE,
- TYPE_CG_RECORD_FIELD,
} TypeCgMode;
typedef struct TypeCgLower {
@@ -772,33 +780,65 @@ static void type_cg_record_memo_put(Pool* p, PoolTypeCache* cache,
(void)c;
if (!cache || !t || id == KIT_CG_TYPE_NONE) return;
if (t->kind != TY_STRUCT && t->kind != TY_UNION) return;
- if (t->rec.incomplete || t->rec.tag_id == TAG_NONE) return;
+ if (t->rec.tag_id == TAG_NONE) return;
CgRecordMap_set(&cache->cg_records, t->rec.tag_id, id);
}
+static KitCgTypeId type_cg_record_decl_id(TypeCgLower* l, const Type* t) {
+ KitCgTypeId id;
+ if (!l || !t || (t->kind != TY_STRUCT && t->kind != TY_UNION)) {
+ return KIT_CG_TYPE_NONE;
+ }
+ id = type_cg_record_memo_get(l->cache, l->c, t);
+ if (id != KIT_CG_TYPE_NONE) return id;
+ id = kit_cg_type_record_decl(l->c, t->rec.tag, t->kind == TY_UNION);
+ type_cg_record_memo_put(l->p, l->cache, l->c, t, id);
+ return id;
+}
+
static KitCgTypeId type_cg_record_layout(TypeCgLower* l, const Type* t) {
KitCgField* fields = NULL;
KitCgRecordDesc desc;
KitCgTypeId id;
+ int tracked = 0;
if (!l || !t || (t->kind != TY_STRUCT && t->kind != TY_UNION)) {
return KIT_CG_TYPE_NONE;
}
+ id = type_cg_record_decl_id(l, t);
+ if (id == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
if (t->rec.incomplete) {
- /* Lowers to void for now but to its real layout once the record is
- * completed in place (type_record_install). Mark the lowering unstable so
- * no enclosing node caches this transient result. */
l->saw_incomplete = 1;
- return type_cg_builtin(l->c, TY_VOID);
+ return id;
+ }
+ if (kit_cg_type_is_complete(l->c, id)) return id;
+ /* Cycle safety net. Recursion *through a pointer* is already broken in the
+ * TY_PTR case (the pointee takes the decl id, never re-entering here), so the
+ * valid self-/mutual-reference shapes never reach this guard. What can still
+ * re-enter mid-completion is a by-value path back to an in-flight record --
+ * e.g. the invalid `struct N { struct N arr[10]; }`, where the array forces
+ * its incomplete element to lay out. Hand back the still-incomplete decl id
+ * (the array then fails the sized-element check, surfacing a clean error)
+ * rather than recursing into a second completion and overflowing the stack.
+ * Track every in-flight record so mutual by-value cycles are covered too.
+ * Anonymous records carry no tag but cannot name themselves, so they never
+ * reach this guard. */
+ if (t->rec.tag_id != TAG_NONE) {
+ if (CgRecordMap_get(&l->cache->cg_records_completing, t->rec.tag_id)) {
+ l->saw_incomplete = 1;
+ return id;
+ }
+ CgRecordMap_set(&l->cache->cg_records_completing, t->rec.tag_id, id);
+ tracked = 1;
}
- id = type_cg_record_memo_get(l->cache, l->c, t);
- if (id != KIT_CG_TYPE_NONE) return id;
if (t->rec.nfields) {
fields = arena_zarray(l->p->arena, KitCgField, t->rec.nfields);
- if (!fields) return KIT_CG_TYPE_NONE;
+ if (!fields) {
+ id = KIT_CG_TYPE_NONE;
+ goto done;
+ }
for (u32 i = 0; i < t->rec.nfields; ++i) {
fields[i].name = t->rec.fields[i].name;
- fields[i].type =
- type_cg_lower(l, t->rec.fields[i].type, TYPE_CG_RECORD_FIELD);
+ fields[i].type = type_cg_lower(l, t->rec.fields[i].type, TYPE_CG_VALUE);
fields[i].align_override = t->rec.fields[i].align_override;
fields[i].max_align = t->rec.fields[i].max_align;
if (t->rec.max_align &&
@@ -826,8 +866,13 @@ static KitCgTypeId type_cg_record_layout(TypeCgLower* l, const Type* t) {
desc.nfields = t->rec.nfields;
desc.is_union = t->kind == TY_UNION;
desc.align_override = t->rec.align_override;
- id = kit_cg_type_record_ex(l->c, &desc);
- type_cg_record_memo_put(l->p, l->cache, l->c, t, id);
+ if (kit_cg_type_record_complete(l->c, id, &desc) != KIT_OK)
+ id = KIT_CG_TYPE_NONE;
+done:
+ /* Clear the in-flight marker so a later, independent lowering of the same tag
+ * (now CG-complete) takes the fast is_complete path rather than this guard.
+ */
+ if (tracked) CgRecordMap_del(&l->cache->cg_records_completing, t->rec.tag_id);
return id;
}
@@ -837,22 +882,19 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
int cacheable;
int outer_incomplete;
if (!l || !t) return KIT_CG_TYPE_NONE;
- /* Only VALUE-mode lowering is memoized on the node: TYPE_CG_RECORD_FIELD
- * collapses pointers to void*, so the same Type* lowers differently there
- * (and that mode is used only transiently for record fields / func params).
- * Check the cache BEFORE the builtin switch: a cached cg_id (whether a builtin
- * id stamped below or a non-builtin lowering) always equals what the switch
- * would recompute, so this hoist only short-circuits already-lowered types —
- * byte-identical, but skips the recompute. */
+ /* Check the cache BEFORE the builtin switch: a cached cg_id (whether a
+ * builtin id stamped below or a non-builtin lowering) always equals what the
+ * switch would recompute, so this hoist only short-circuits already-lowered
+ * types -- byte-identical, but skips the recompute. */
cacheable = (mode == TYPE_CG_VALUE);
if (cacheable && t->cg_id != KIT_CG_TYPE_NONE)
return t->cg_id; /* cached => was incomplete-free => still stable */
id = type_cg_builtin(l->c, (TypeKind)t->kind);
if (id != KIT_CG_TYPE_NONE) {
/* type_cg_builtin is a pure function of t->kind (and the fixed target), so
- * a builtin id is mode-independent and incomplete-free: stamp it on the node
- * so the next VALUE-mode crossing short-circuits above. A builtin id is
- * never 0 (builtin_id(VOID)=64), so it can't alias the NONE sentinel. */
+ * a builtin id is mode-independent and incomplete-free: stamp it on the
+ * node so the next VALUE-mode crossing short-circuits above. Builtins are
+ * never 0, so they can't alias the NONE sentinel. */
if (cacheable) ((Type*)t)->cg_id = id;
return id;
}
@@ -861,14 +903,29 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
outer_incomplete = l->saw_incomplete;
l->saw_incomplete = 0;
switch ((TypeKind)t->kind) {
- case TY_PTR:
- if (mode == TYPE_CG_RECORD_FIELD) {
- id = kit_cg_type_ptr(l->c, type_cg_builtin(l->c, TY_VOID), 0);
+ case TY_PTR: {
+ /* A pointer needs only its pointee's nominal identity, never its layout,
+ * so a record/union pointee lowers to its (possibly incomplete) decl id
+ * instead of being completed here. This is what makes a forward-declared
+ * record used only through pointers legal without ever completing it, and
+ * it breaks self-/mutual-reference cycles (struct N { struct N* next; },
+ * A<->B) at the pointer rather than recursing into completion. A record
+ * is completed only when something needs its layout -- a by-value local,
+ * field, param/result, array element, sizeof, or member access -- all of
+ * which lower the record itself, not a pointer to it. */
+ const Type* pointee = t->ptr.pointee;
+ KitCgTypeId pid;
+ if (pointee->kind == TY_STRUCT || pointee->kind == TY_UNION) {
+ pid = type_cg_record_decl_id(l, pointee);
+ if (pid != KIT_CG_TYPE_NONE && !kit_cg_type_is_complete(l->c, pid))
+ l->saw_incomplete = 1;
} else {
- id = kit_cg_type_ptr(l->c,
- type_cg_lower(l, t->ptr.pointee, TYPE_CG_VALUE), 0);
+ pid = type_cg_lower(l, pointee, TYPE_CG_VALUE);
}
+ id = pid == KIT_CG_TYPE_NONE ? KIT_CG_TYPE_NONE
+ : kit_cg_type_ptr(l->c, pid, 0);
break;
+ }
case TY_ARRAY:
id = kit_cg_type_array(l->c, type_cg_lower(l, t->arr.elem, mode),
t->arr.count);
@@ -877,8 +934,7 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
KitCgFuncParam* params = NULL;
KitCgFuncSig sig;
memset(&sig, 0, sizeof sig);
- if (t->fn.ret->kind != TY_VOID)
- sig.result.type = type_cg_lower(l, t->fn.ret, TYPE_CG_VALUE);
+ sig.result.type = type_cg_lower(l, t->fn.ret, TYPE_CG_VALUE);
sig.nparams = t->fn.nparams;
sig.abi_variadic = t->fn.variadic;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -889,8 +945,7 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
break;
}
for (u32 i = 0; i < t->fn.nparams; ++i) {
- params[i].type =
- type_cg_lower(l, t->fn.params[i], TYPE_CG_RECORD_FIELD);
+ params[i].type = type_cg_lower(l, t->fn.params[i], TYPE_CG_VALUE);
}
}
sig.params = params;
@@ -931,4 +986,3 @@ KitCgTypeId type_cg_id_in_pool(KitCompiler* c, Pool* p, const Type* t) {
l.saw_incomplete = 0;
return type_cg_lower(&l, t, TYPE_CG_VALUE);
}
-
diff --git a/lang/toy/builtins.c b/lang/toy/builtins.c
@@ -1060,7 +1060,7 @@ KitCgTypeId toy_parse_generic_builtin(ToyParser* p, KitSym name,
fields[0].type = ty;
fields[1].name = kit_sym_intern(p->c, KIT_SLICE_LIT("overflow"));
fields[1].type = toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
- rec_ty = kit_cg_type_record(p->c, 0, fields, 2);
+ rec_ty = toy_cg_record_type(p, 0, fields, 2, 0, 0);
rec_slot = kit_cg_local(p->cg, rec_ty, toy_slot_attrs(0));
{
uint64_t f0_off = 0, f1_off = 0;
@@ -1367,7 +1367,7 @@ KitCgTypeId toy_parse_atomic_generic_builtin(ToyParser* p, KitSym name,
fields[0].type = ty;
fields[1].name = kit_sym_intern(p->c, KIT_SLICE_LIT("ok"));
fields[1].type = toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
- rec_ty = kit_cg_type_record(p->c, 0, fields, 2);
+ rec_ty = toy_cg_record_type(p, 0, fields, 2, 0, 0);
rec_slot = kit_cg_local(p->cg, rec_ty, toy_slot_attrs(0));
{
uint64_t f0_off = 0, f1_off = 0;
diff --git a/lang/toy/decls.c b/lang/toy/decls.c
@@ -33,7 +33,7 @@ int toy_parse_record_decl(ToyParser* p) {
size_t nfields = 0;
size_t cap_fields = 0;
size_t cap_field_infos = 0;
- KitCgTypeId type;
+ KitCgTypeId type = KIT_CG_TYPE_NONE;
ToyNamedType* named;
int packed = 0;
uint32_t record_align = 0;
@@ -47,15 +47,42 @@ int toy_parse_record_decl(ToyParser* p) {
}
name = toy_tok_sym(p, p->cur);
toy_parser_advance(p);
+ named = toy_find_named_type(p, name);
if (toy_parser_match(p, TOK_SEMI)) {
- return toy_add_named_type(p, name, KIT_CG_TYPE_NONE, TOY_NAMED_RECORD,
- KIT_CG_TYPE_NONE);
+ if (named) return 1;
+ type = kit_cg_type_record_decl(p->c, name, 0);
+ if (type == KIT_CG_TYPE_NONE) {
+ toy_error(p, p->cur.loc, "failed to declare record type");
+ return 0;
+ }
+ return toy_add_named_type(p, name, type, TOY_NAMED_RECORD, type);
}
- if (!toy_find_named_type(p, name)) {
- if (!toy_add_named_type(p, name, KIT_CG_TYPE_NONE, TOY_NAMED_RECORD,
- KIT_CG_TYPE_NONE)) {
+ if (named) {
+ if (named->kind != TOY_NAMED_RECORD) {
+ toy_error(p, p->cur.loc, "record name already used for another type");
return 0;
}
+ type = named->type;
+ if (type == KIT_CG_TYPE_NONE) {
+ type = kit_cg_type_record_decl(p->c, name, 0);
+ if (type == KIT_CG_TYPE_NONE ||
+ !toy_add_named_type(p, name, type, TOY_NAMED_RECORD, type)) {
+ toy_error(p, p->cur.loc, "failed to declare record type");
+ return 0;
+ }
+ named = toy_find_named_type(p, name);
+ } else if (kit_cg_type_is_complete(p->c, type)) {
+ toy_error(p, p->cur.loc, "record type redefinition");
+ return 0;
+ }
+ } else {
+ type = kit_cg_type_record_decl(p->c, name, 0);
+ if (type == KIT_CG_TYPE_NONE ||
+ !toy_add_named_type(p, name, type, TOY_NAMED_RECORD, type)) {
+ toy_error(p, p->cur.loc, "failed to declare record type");
+ return 0;
+ }
+ named = toy_find_named_type(p, name);
}
if (!toy_parser_expect(p, TOK_LBRACE)) {
toy_error(p, p->cur.loc, "expected '{' after record name");
@@ -98,23 +125,11 @@ int toy_parse_record_decl(ToyParser* p) {
if (!fields[i].align_override) fields[i].align_override = 1;
}
}
- if (record_align) {
- KitCgRecordDesc desc;
- memset(&desc, 0, sizeof desc);
- desc.tag = name;
- desc.fields = fields;
- desc.nfields = (uint32_t)nfields;
- desc.align_override = record_align;
- type = kit_cg_type_record_ex(p->c, &desc);
- } else {
- type = kit_cg_type_record(p->c, name, fields, (uint32_t)nfields);
- }
- if (type == KIT_CG_TYPE_NONE) {
+ if (!toy_cg_record_complete(p, type, name, fields, (uint32_t)nfields, 0,
+ record_align)) {
toy_error(p, p->cur.loc, "failed to create record type");
goto done;
}
- if (!toy_add_named_type(p, name, type, TOY_NAMED_RECORD, type)) goto done;
- named = toy_find_named_type(p, name);
ok = named && toy_set_named_type_fields(p, named, field_infos, nfields);
done:
@@ -169,7 +184,7 @@ int toy_parse_tuple_decl(ToyParser* p) {
toy_error(p, p->cur.loc, "expected '}' after tuple declaration");
goto done;
}
- type = kit_cg_type_record(p->c, name, fields, (uint32_t)nfields);
+ type = toy_cg_record_type(p, name, fields, (uint32_t)nfields, 0, 0);
if (type == KIT_CG_TYPE_NONE) {
toy_error(p, p->cur.loc, "failed to create tuple type");
goto done;
@@ -426,10 +441,8 @@ int toy_parse_fn(ToyParser* p, int is_extern, int is_pub) {
sig_params[i].attrs = param_attrs[i];
}
memset(&sig, 0, sizeof sig);
- if (kit_cg_type_kind(p->c, ret_type) != KIT_CG_TYPE_VOID) {
- sig.result.type = ret_type;
- sig.result.attrs = ret_attrs;
- }
+ sig.result.type = ret_type;
+ sig.result.attrs = ret_attrs;
sig.params = sig_params;
sig.nparams = (uint32_t)nparams;
sig.call_conv = attrs.has_call_conv ? attrs.call_conv : KIT_CG_CC_TARGET_C;
diff --git a/lang/toy/internal.h b/lang/toy/internal.h
@@ -233,7 +233,6 @@ typedef struct ToyParser {
ToyToken cur;
KitCompiler* c;
KitCg* cg;
- KitCgBuiltinTypes types;
KitCgTypeId int_type;
KitCgTypeId size_type;
KitCgTypeId int_ptr_type;
@@ -285,6 +284,12 @@ typedef struct ToyParser {
} ToyParser;
KitCgTypeId toy_builtin_type(ToyParser* p, KitCgBuiltinType ty);
+KitCgTypeId toy_cg_record_type(ToyParser* p, KitSym tag,
+ const KitCgField* fields, uint32_t nfields,
+ int is_union, uint32_t align_override);
+int toy_cg_record_complete(ToyParser* p, KitCgTypeId record, KitSym tag,
+ const KitCgField* fields, uint32_t nfields,
+ int is_union, uint32_t align_override);
/* The toy frontend models a single return value: the function's first result
* type, or void when it has no results. */
KitCgTypeId toy_cg_func_ret(ToyParser* p, KitCgTypeId fn_ty);
diff --git a/lang/toy/parser.c b/lang/toy/parser.c
@@ -602,7 +602,7 @@ static int toy_parse_while_initializer_named(ToyParser* p, KitCgLocal slot,
p->nscopes + 1u, sizeof *p->scopes, "scopes")) {
return 0;
}
- scope = kit_cg_scope_begin(p->cg, result_ty);
+ scope = kit_cg_scope_begin_value(p->cg, result_ty);
p->scopes[p->nscopes].name = label_name;
p->scopes[p->nscopes].kind = TOY_SCOPE_LOOP;
p->scopes[p->nscopes].cg_scope = scope;
@@ -1017,7 +1017,7 @@ static int toy_parse_while_stmt_named(ToyParser* p, KitSym label_name) {
p->nscopes + 1u, sizeof *p->scopes, "scopes")) {
return 0;
}
- scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
+ scope = kit_cg_scope_begin(p->cg);
p->scopes[p->nscopes].name = label_name;
p->scopes[p->nscopes].kind = TOY_SCOPE_LOOP;
p->scopes[p->nscopes].cg_scope = scope;
@@ -1086,7 +1086,7 @@ static int toy_parse_switch_stmt_named(ToyParser* p, KitSym label_name) {
* block is reachable from the straight-line predecessor. With the
* jump before scope_begin, opt's CFG sees the scope_begin block as
* unreachable and prunes it; scope_end then can't find its handle. */
- scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
+ scope = kit_cg_scope_begin(p->cg);
p->scopes[p->nscopes].name = label_name;
p->scopes[p->nscopes].kind = TOY_SCOPE_SWITCH;
p->scopes[p->nscopes].cg_scope = scope;
diff --git a/lang/toy/parser_core.c b/lang/toy/parser_core.c
@@ -5,7 +5,33 @@
#include "internal.h"
KitCgTypeId toy_builtin_type(ToyParser* p, KitCgBuiltinType ty) {
- return p->types.id[ty];
+ return kit_cg_type_builtin(p->c, ty);
+}
+
+KitCgTypeId toy_cg_record_type(ToyParser* p, KitSym tag,
+ const KitCgField* fields, uint32_t nfields,
+ int is_union, uint32_t align_override) {
+ KitCgRecordDesc desc;
+ memset(&desc, 0, sizeof desc);
+ desc.tag = tag;
+ desc.fields = fields;
+ desc.nfields = nfields;
+ desc.is_union = is_union;
+ desc.align_override = align_override;
+ return kit_cg_type_record(p->c, &desc);
+}
+
+int toy_cg_record_complete(ToyParser* p, KitCgTypeId record, KitSym tag,
+ const KitCgField* fields, uint32_t nfields,
+ int is_union, uint32_t align_override) {
+ KitCgRecordDesc desc;
+ memset(&desc, 0, sizeof desc);
+ desc.tag = tag;
+ desc.fields = fields;
+ desc.nfields = nfields;
+ desc.is_union = is_union;
+ desc.align_override = align_override;
+ return kit_cg_type_record_complete(p->c, record, &desc) == KIT_OK;
}
KitCgTypeId toy_cg_func_ret(ToyParser* p, KitCgTypeId fn_ty) {
@@ -57,7 +83,6 @@ void toy_parser_init(ToyParser* p, KitCompiler* c, KitCg* cg, ToyModule* module,
p->cur = toy_lexer_next(&p->lex);
p->c = c;
p->cg = cg;
- p->types = kit_cg_builtin_types(c);
p->target = kit_compiler_target_spec(c);
p->int_type = toy_builtin_type(p, KIT_CG_BUILTIN_I64);
p->size_type = toy_builtin_type(
@@ -100,7 +125,6 @@ void toy_parser_reinit(ToyParser* p, KitCompiler* c, KitCg* cg,
p->cur = toy_lexer_next(&p->lex);
p->c = c;
p->cg = cg;
- p->types = kit_cg_builtin_types(c);
p->target = kit_compiler_target_spec(c);
p->int_type = toy_builtin_type(p, KIT_CG_BUILTIN_I64);
p->size_type = toy_builtin_type(
diff --git a/lang/toy/types.c b/lang/toy/types.c
@@ -159,8 +159,7 @@ KitCgTypeId toy_parse_type(ToyParser* p) {
for (i = 0; i < nparams; i++) sig_params[i].type = param_types[i];
memset(&sig, 0, sizeof sig);
- if (kit_cg_type_kind(p->c, ret_type) != KIT_CG_TYPE_VOID)
- sig.result.type = ret_type;
+ sig.result.type = ret_type;
sig.params = sig_params;
sig.nparams = (uint32_t)nparams;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -255,20 +254,9 @@ KitCgTypeId toy_parse_type(ToyParser* p) {
if (!fields[i].align_override) fields[i].align_override = 1;
}
}
- if (record_align) {
- KitCgRecordDesc desc;
- KitCgTypeId record_ty;
- memset(&desc, 0, sizeof desc);
- desc.fields = fields;
- desc.nfields = (uint32_t)nfields;
- desc.align_override = record_align;
- record_ty = kit_cg_type_record_ex(p->c, &desc);
- result = toy_type_finish(p, record_ty, toy_type_from_cg(p, record_ty));
- goto record_done;
- }
{
KitCgTypeId record_ty =
- kit_cg_type_record(p->c, 0, fields, (uint32_t)nfields);
+ toy_cg_record_type(p, 0, fields, (uint32_t)nfields, 0, record_align);
result = toy_type_finish(p, record_ty, toy_type_from_cg(p, record_ty));
}
record_done:
@@ -331,7 +319,10 @@ KitCgTypeId toy_parse_type(ToyParser* p) {
else {
ToyNamedType* named = toy_find_named_type(p, toy_tok_sym(p, p->cur));
if (named) {
- if (named->type == KIT_CG_TYPE_NONE) {
+ if (named->type == KIT_CG_TYPE_NONE ||
+ ((named->kind == TOY_NAMED_RECORD ||
+ named->kind == TOY_NAMED_TUPLE) &&
+ !kit_cg_type_is_complete(p->c, named->type))) {
toy_error(p, p->cur.loc, "incomplete record type requires pointer");
return KIT_CG_TYPE_NONE;
}
@@ -366,11 +357,14 @@ KitCgTypeId toy_parse_type(ToyParser* p) {
}
if (p->cur.kind == TOK_IDENT) {
incomplete_pointee = toy_find_named_type(p, toy_tok_sym(p, p->cur));
- if (incomplete_pointee && incomplete_pointee->type == KIT_CG_TYPE_NONE) {
+ if (incomplete_pointee &&
+ (incomplete_pointee->kind == TOY_NAMED_RECORD ||
+ incomplete_pointee->kind == TOY_NAMED_TUPLE) &&
+ incomplete_pointee->type != KIT_CG_TYPE_NONE &&
+ !kit_cg_type_is_complete(p->c, incomplete_pointee->type)) {
KitCgTypeId ptr_ty;
toy_parser_advance(p);
- ptr_ty = kit_cg_type_ptr(p->c, toy_builtin_type(p, KIT_CG_BUILTIN_VOID),
- address_space);
+ ptr_ty = kit_cg_type_ptr(p->c, incomplete_pointee->type, address_space);
return toy_type_finish(
p, ptr_ty,
toy_type_register_ptr(p, ptr_ty, incomplete_pointee->toy_type,
@@ -428,7 +422,7 @@ KitCgTypeId toy_ptr_pointee_func_type(ToyParser* p, KitCgTypeId ptr_ty) {
void toy_type_register_builtins(ToyParser* p) {
size_t i;
for (i = 0; i < KIT_CG_BUILTIN_COUNT; ++i)
- (void)toy_type_from_cg(p, p->types.id[i]);
+ (void)toy_type_from_cg(p, toy_builtin_type(p, (KitCgBuiltinType)i));
(void)toy_type_from_cg(p, p->int_ptr_type);
}
@@ -584,7 +578,7 @@ ToyTypeId toy_type_register_slice(ToyParser* p, KitCgTypeId elem_cg,
fields[1].type = p->size_type;
memset(&type, 0, sizeof type);
type.kind = TOY_TYPE_SLICE;
- type.cg = kit_cg_type_record(p->c, 0, fields, 2);
+ type.cg = toy_cg_record_type(p, 0, fields, 2, 0, 0);
type.elem = elem;
return toy_type_add(p, &type);
}
diff --git a/lang/wasm/cg.c b/lang/wasm/cg.c
@@ -3,7 +3,30 @@
#include "runtime_abi.h"
#include "wasm/wasm.h"
-static KitCgTypeId wasm_cg_type(KitCompiler* c, KitCgBuiltinTypes b,
+typedef struct WasmCgBuiltinTypes {
+ KitCgTypeId id[KIT_CG_BUILTIN_COUNT];
+} WasmCgBuiltinTypes;
+
+static WasmCgBuiltinTypes wasm_cg_builtin_types(KitCompiler* c) {
+ WasmCgBuiltinTypes b;
+ memset(&b, 0, sizeof b);
+ for (uint32_t i = 0; i < KIT_CG_BUILTIN_COUNT; ++i)
+ b.id[i] = kit_cg_type_builtin(c, (KitCgBuiltinType)i);
+ return b;
+}
+
+static KitCgTypeId wasm_cg_record_type(KitCompiler* c, KitSym tag,
+ const KitCgField* fields,
+ uint32_t nfields) {
+ KitCgRecordDesc desc;
+ memset(&desc, 0, sizeof desc);
+ desc.tag = tag;
+ desc.fields = fields;
+ desc.nfields = nfields;
+ return kit_cg_type_record(c, &desc);
+}
+
+static KitCgTypeId wasm_cg_type(KitCompiler* c, WasmCgBuiltinTypes b,
WasmValType vt) {
switch (vt) {
case WASM_VAL_I32:
@@ -22,7 +45,7 @@ static KitCgTypeId wasm_cg_type(KitCompiler* c, KitCgBuiltinTypes b,
return b.id[KIT_CG_BUILTIN_I32];
}
-static KitCgMemAccess wasm_cg_mem(KitCompiler* c, KitCgBuiltinTypes b,
+static KitCgMemAccess wasm_cg_mem(KitCompiler* c, WasmCgBuiltinTypes b,
WasmValType vt) {
KitCgMemAccess mem;
memset(&mem, 0, sizeof mem);
@@ -37,7 +60,7 @@ static KitCgMemAccess wasm_cg_mem_type(KitCgTypeId ty) {
return mem;
}
-static void wasm_cg_push_zero(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_push_zero(KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b,
WasmValType vt) {
KitCgTypeId ty = wasm_cg_type(c, b, vt);
if (wasm_is_ref_type(vt))
@@ -48,7 +71,7 @@ static void wasm_cg_push_zero(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
kit_cg_push_int(cg, 0, ty);
}
-static KitCgTypeId wasm_load_storage_type(KitCgBuiltinTypes b, uint8_t kind) {
+static KitCgTypeId wasm_load_storage_type(WasmCgBuiltinTypes b, uint8_t kind) {
switch (kind) {
case WASM_INSN_I32_LOAD8_S:
case WASM_INSN_I32_LOAD8_U:
@@ -73,7 +96,7 @@ static KitCgTypeId wasm_load_storage_type(KitCgBuiltinTypes b, uint8_t kind) {
}
}
-static KitCgTypeId wasm_store_storage_type(KitCgBuiltinTypes b, uint8_t kind) {
+static KitCgTypeId wasm_store_storage_type(WasmCgBuiltinTypes b, uint8_t kind) {
switch (kind) {
case WASM_INSN_I32_STORE8:
case WASM_INSN_I64_STORE8:
@@ -168,6 +191,7 @@ typedef struct WasmCgRuntime {
uint32_t* passive_elem_storage_field;
uint32_t passive_elem_entries_field;
uint32_t passive_elem_length_field;
+ KitCgTypeId void_ty;
KitCgTypeId trap_func_ty;
KitCgSym trap_syms[WASM_TRAP_COUNT];
} WasmCgRuntime;
@@ -191,8 +215,9 @@ static const char* wasm_trap_name(WasmTrapKind kind) {
}
}
-static void wasm_cg_emit_raw_trap(KitCg* cg) {
- kit_cg_intrinsic(cg, KIT_CG_INTRIN_TRAP, 0, KIT_CG_TYPE_NONE);
+static void wasm_cg_emit_raw_trap(KitCompiler* c, KitCg* cg) {
+ kit_cg_intrinsic(cg, KIT_CG_INTRIN_TRAP, 0,
+ kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID));
kit_cg_unreachable(cg);
}
@@ -201,8 +226,8 @@ static void wasm_cg_trap(KitCg* cg, const WasmCgRuntime* rt,
if (rt && kind < WASM_TRAP_COUNT && rt->trap_syms[kind])
kit_cg_call_symbol(cg, rt->trap_syms[kind], 0,
(KitCgCallAttrs){.flags = KIT_CG_CALL_COLD});
- else
- kit_cg_intrinsic(cg, KIT_CG_INTRIN_TRAP, 0, KIT_CG_TYPE_NONE);
+ else if (rt)
+ kit_cg_intrinsic(cg, KIT_CG_INTRIN_TRAP, 0, rt->void_ty);
kit_cg_unreachable(cg);
}
@@ -255,7 +280,7 @@ static uint32_t wasm_cg_checked_add_u32(KitCompiler* c, uint32_t a, uint32_t b,
return a + b;
}
-static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
+static void wasm_cg_build_runtime(KitCompiler* c, WasmCgBuiltinTypes b,
const WasmModule* m, WasmCgRuntime* rt,
KitArena* arena) {
KitCgField memory_fields[4];
@@ -344,6 +369,7 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
m->ntables ? kit_arena_zarray(arena, uint32_t, m->ntables) : NULL;
table_entries_field_idx =
m->ntables ? kit_arena_zarray(arena, uint32_t, m->ntables) : NULL;
+ rt->void_ty = b.id[KIT_CG_BUILTIN_VOID];
rt->i8_ptr_ty = kit_cg_type_ptr(c, b.id[KIT_CG_BUILTIN_I8], 0);
rt->void_ptr_ty = rt->i8_ptr_ty;
memset(memory_fields, 0, sizeof memory_fields);
@@ -355,7 +381,7 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
memory_fields[2].type = b.id[KIT_CG_BUILTIN_I64];
memory_fields[3].name = kit_sym_intern(c, KIT_SLICE_LIT("flags"));
memory_fields[3].type = b.id[KIT_CG_BUILTIN_I32];
- rt->memory_ty = kit_cg_type_record(
+ rt->memory_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmMemory")), memory_fields, 4);
rt->memory_data_offset = wasm_cg_field_offset(c, rt->memory_ty, 0);
rt->memory_pages_offset = wasm_cg_field_offset(c, rt->memory_ty, 1);
@@ -368,14 +394,14 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
memset(func_import_fields, 0, sizeof func_import_fields);
func_import_fields[0].name = kit_sym_intern(c, KIT_SLICE_LIT("fn"));
func_import_fields[0].type = rt->void_ptr_ty;
- rt->func_import_ty = kit_cg_type_record(
+ rt->func_import_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmFuncImport")),
func_import_fields, 1);
rt->func_import_fn_offset = wasm_cg_field_offset(c, rt->func_import_ty, 0);
memset(global_import_fields, 0, sizeof global_import_fields);
global_import_fields[0].name = kit_sym_intern(c, KIT_SLICE_LIT("addr"));
global_import_fields[0].type = rt->void_ptr_ty;
- rt->global_import_ty = kit_cg_type_record(
+ rt->global_import_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmGlobalImport")),
global_import_fields, 1);
rt->global_import_addr_offset =
@@ -385,7 +411,7 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
table_entry_fields[0].type = rt->void_ptr_ty;
table_entry_fields[1].name = kit_sym_intern(c, KIT_SLICE_LIT("typeidx"));
table_entry_fields[1].type = b.id[KIT_CG_BUILTIN_I32];
- rt->table_entry_ty = kit_cg_type_record(
+ rt->table_entry_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmTableEntry")),
table_entry_fields, 2);
rt->table_entry_ptr_ty = kit_cg_type_ptr(c, rt->table_entry_ty, 0);
@@ -402,7 +428,7 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
table_fields[1].type = b.id[KIT_CG_BUILTIN_I32];
table_fields[2].name = kit_sym_intern(c, KIT_SLICE_LIT("max"));
table_fields[2].type = b.id[KIT_CG_BUILTIN_I32];
- rt->table_ty = kit_cg_type_record(
+ rt->table_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmTable")), table_fields, 3);
rt->table_entries_ptr_offset = wasm_cg_field_offset(c, rt->table_ty, 0);
rt->table_len_offset = wasm_cg_field_offset(c, rt->table_ty, 1);
@@ -415,7 +441,7 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
passive_data_fields[0].type = rt->i8_ptr_ty;
passive_data_fields[1].name = kit_sym_intern(c, KIT_SLICE_LIT("len"));
passive_data_fields[1].type = b.id[KIT_CG_BUILTIN_I64];
- rt->passive_data_ty = kit_cg_type_record(
+ rt->passive_data_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmPassiveDataSegment")),
passive_data_fields, 2);
rt->passive_data_base_field = 0;
@@ -425,7 +451,7 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
passive_elem_fields[0].type = rt->table_entry_ptr_ty;
passive_elem_fields[1].name = kit_sym_intern(c, KIT_SLICE_LIT("length"));
passive_elem_fields[1].type = b.id[KIT_CG_BUILTIN_I32];
- rt->passive_elem_ty = kit_cg_type_record(
+ rt->passive_elem_ty = wasm_cg_record_type(
c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmPassiveElemSegment")),
passive_elem_fields, 2);
rt->passive_elem_entries_field = 0;
@@ -514,9 +540,9 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
kit_cg_type_array(c, rt->table_entry_ty, nfuncs ? nfuncs : 1u);
nfields++;
}
- rt->instance_ty =
- kit_cg_type_record(c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmInstance")),
- instance_fields, nfields);
+ rt->instance_ty = wasm_cg_record_type(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("KitWasmInstance")), instance_fields,
+ nfields);
rt->instance_ptr_ty = kit_cg_type_ptr(c, rt->instance_ty, 0);
for (uint32_t i = 0; i < m->nmemories; ++i)
rt->memory_offset[i] =
@@ -558,7 +584,7 @@ static void wasm_cg_push_instance_lvalue(KitCg* cg, const WasmCgRuntime* rt,
/* Add a constant byte offset to the pointer rvalue on TOS, retyping to
* `result_ptr_ty`. No-op when offset == 0 (and the type is not retyped). */
-static void wasm_cg_ptr_add_offset(KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_ptr_add_offset(KitCg* cg, WasmCgBuiltinTypes b,
uint64_t offset, KitCgTypeId result_ptr_ty) {
KitCgTypeId i64_ty = b.id[KIT_CG_BUILTIN_I64];
if (offset == 0) {
@@ -603,7 +629,7 @@ static void wasm_cg_push_import_func_ptr(KitCg* cg, const WasmCgRuntime* rt,
* dereferencing the import indirection if needed. Result type is T* where T is
* the global's value type. */
static void wasm_cg_push_global_value_ptr(
- KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b, const WasmCgRuntime* rt,
+ KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b, const WasmCgRuntime* rt,
KitCgLocal instance_local, const WasmModule* m, uint32_t global_index) {
KitCgTypeId ptr_ty =
kit_cg_type_ptr(c, wasm_cg_type(c, b, m->globals[global_index].type), 0);
@@ -651,7 +677,7 @@ static void wasm_cg_push_table_entry_lvalue(KitCg* cg, const WasmCgRuntime* rt,
/* Push a pointer rvalue to instance->tables[table_index].entries[index_local].
* The index is loaded from a temp local supplied by the caller (mirrors the
* previous helper's signature). */
-static void wasm_cg_push_table_entry_ptr(KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_push_table_entry_ptr(KitCg* cg, WasmCgBuiltinTypes b,
const WasmCgRuntime* rt,
KitCgLocal instance_local,
uint32_t table_index,
@@ -695,8 +721,9 @@ static void wasm_cg_push_passive_elem_storage_array_lvalue(
kit_cg_field(cg, rt->passive_elem_storage_field[elemidx]);
}
-static void wasm_cg_memory_check(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
- const WasmModule* m, const WasmCgRuntime* rt,
+static void wasm_cg_memory_check(KitCompiler* c, KitCg* cg,
+ WasmCgBuiltinTypes b, const WasmModule* m,
+ const WasmCgRuntime* rt,
KitCgLocal instance_local,
const WasmInsn* in) {
uint32_t width = wasm_mem_width(in->kind);
@@ -737,7 +764,7 @@ static void wasm_cg_memory_check(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
/* Compute the absolute address (data_ptr + addr_on_tos + offset) as a
* pointer rvalue. Stack: [addr] -> [void*]. */
-static void wasm_cg_memory_addr_from_tos(KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_memory_addr_from_tos(KitCg* cg, WasmCgBuiltinTypes b,
const WasmCgRuntime* rt,
const WasmModule* m,
KitCgLocal instance_local,
@@ -756,7 +783,7 @@ static void wasm_cg_memory_addr_from_tos(KitCg* cg, KitCgBuiltinTypes b,
kit_cg_int_to_ptr(cg, rt->i8_ptr_ty);
}
-static void wasm_cg_rotate(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_rotate(KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b,
WasmValType vt, int right) {
KitCgTypeId ty = wasm_cg_type(c, b, vt);
KitCgMemAccess mem = wasm_cg_mem(c, b, vt);
@@ -795,8 +822,9 @@ static void wasm_cg_rotate(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
}
static void wasm_cg_checked_divrem(KitCompiler* c, KitCg* cg,
- KitCgBuiltinTypes b, const WasmCgRuntime* rt,
- WasmValType vt, KitCgIntBinOp op) {
+ WasmCgBuiltinTypes b,
+ const WasmCgRuntime* rt, WasmValType vt,
+ KitCgIntBinOp op) {
KitCgTypeId ty = wasm_cg_type(c, b, vt);
KitCgMemAccess mem = wasm_cg_mem(c, b, vt);
KitCgLocalAttrs attrs;
@@ -844,7 +872,7 @@ static void wasm_cg_checked_divrem(KitCompiler* c, KitCg* cg,
}
static void wasm_cg_checked_trunc(KitCompiler* c, KitCg* cg,
- KitCgBuiltinTypes b, const WasmCgRuntime* rt,
+ WasmCgBuiltinTypes b, const WasmCgRuntime* rt,
WasmValType src, WasmValType dst,
int is_unsigned) {
KitCgTypeId src_ty = wasm_cg_type(c, b, src);
@@ -1022,7 +1050,7 @@ static int wasm_fp_cmp_op(uint8_t kind, KitCgFpCmpOp* out) {
}
}
-static void wasm_cg_call_func(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_call_func(KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b,
const WasmFunc* f, const WasmCgRuntime* rt,
KitCgSym sym, KitCgTypeId func_type,
KitCgLocal instance_local, uint32_t func_index,
@@ -1082,7 +1110,7 @@ static void wasm_cg_call_func(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
/* Intern a NUL-terminated string as readonly const data and return its
* symbol. Each call mints a fresh symbol; the caller is responsible for
* deduplication if that matters. */
-static KitCgSym wasm_cg_intern_cstr(KitCg* cg, KitCgBuiltinTypes b,
+static KitCgSym wasm_cg_intern_cstr(KitCg* cg, WasmCgBuiltinTypes b,
const char* s) {
size_t len = 0;
while (s && s[len]) ++len;
@@ -1095,7 +1123,7 @@ static KitCgSym wasm_cg_intern_cstr(KitCg* cg, KitCgBuiltinTypes b,
* count (=0) is emitted; descriptor/type arrays are omitted so the binder can
* no-op early via kit_jit_lookup returning NULL. */
static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
- KitCgBuiltinTypes b,
+ WasmCgBuiltinTypes b,
const WasmModule* m,
const WasmCgRuntime* rt,
KitArena* arena) {
@@ -1508,7 +1536,7 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
* The memory-layout array intentionally contains only fixed-width integers, so
* the host reader can consume it without target pointer-size ambiguity. */
static void wasm_cg_emit_runtime_layout_metadata(KitCompiler* c, KitCg* cg,
- KitCgBuiltinTypes b,
+ WasmCgBuiltinTypes b,
const WasmModule* m,
const WasmCgRuntime* rt) {
KitCgTypeId u8_ty = b.id[KIT_CG_BUILTIN_I8];
@@ -1598,7 +1626,7 @@ static void wasm_cg_emit_runtime_layout_metadata(KitCompiler* c, KitCg* cg,
/* Bounds-check that addr + n <= size. addr/n/size are all treated as i64
* (caller zero-extends i32 inputs first). Traps on overflow or out-of-range
* via the rt bounds trap; falls through on success. */
-static void wasm_cg_bulk_bounds_check(KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_bulk_bounds_check(KitCg* cg, WasmCgBuiltinTypes b,
const WasmCgRuntime* rt,
KitCgLocal addr_local, KitCgLocal n_local,
KitCgLocal size_local) {
@@ -1633,7 +1661,7 @@ static void wasm_cg_bulk_bounds_check(KitCg* cg, KitCgBuiltinTypes b,
* pointer comparison. Locals are pre-stored i64 values; src_base and
* dst_base are i8* values pre-stored in locals. */
static void wasm_cg_emit_byte_copy_loop(
- KitCg* cg, KitCgBuiltinTypes b, const WasmCgRuntime* rt,
+ KitCg* cg, WasmCgBuiltinTypes b, const WasmCgRuntime* rt,
KitCgLocal dst_base_local, KitCgLocal src_base_local,
KitCgLocal dst_addr_local, KitCgLocal src_addr_local, KitCgLocal n_local) {
KitCgMemAccess i64_mem = wasm_cg_mem_type(b.id[KIT_CG_BUILTIN_I64]);
@@ -1757,7 +1785,7 @@ static void wasm_cg_emit_byte_copy_loop(
}
/* Emit a byte-fill loop: writes (val & 0xff) into dst_base+dst_addr..+n. */
-static void wasm_cg_emit_byte_fill_loop(KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_emit_byte_fill_loop(KitCg* cg, WasmCgBuiltinTypes b,
const WasmCgRuntime* rt,
KitCgLocal dst_base_local,
KitCgLocal dst_addr_local,
@@ -1808,7 +1836,7 @@ static void wasm_cg_emit_byte_fill_loop(KitCg* cg, KitCgBuiltinTypes b,
/* Like wasm_cg_emit_byte_copy_loop but for table entries (struct-sized). */
static void wasm_cg_emit_table_copy_loop(
- KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b, const WasmCgRuntime* rt,
+ KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b, const WasmCgRuntime* rt,
KitCgLocal dst_base_local, KitCgLocal src_base_local,
KitCgLocal dst_idx_local, KitCgLocal src_idx_local, KitCgLocal n_local) {
KitCgMemAccess i64_mem = wasm_cg_mem_type(b.id[KIT_CG_BUILTIN_I64]);
@@ -1970,7 +1998,7 @@ static void wasm_cg_emit_table_copy_loop(
}
static void wasm_cg_cache_funcref_entry(
- KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b, const WasmCgRuntime* rt,
+ KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b, const WasmCgRuntime* rt,
KitCgLocal ref_local, KitCgLocal fn_local, KitCgLocal typeidx_local,
KitCgMemAccess ref_mem, KitCgMemAccess i32_mem) {
KitCgLabel is_null = kit_cg_label_new(cg);
@@ -2018,13 +2046,13 @@ static void wasm_cg_cache_funcref_entry(
*
* A frame opened in dead code (after a branch terminator, before the matching
* else/end) is marked `entry_dead` and owns no CG scope — it only tracks
- * nesting so the matching end pops the right depth. `base` is the CG value-stack
- * depth just below the block's result region, used to discard the operands a
- * Wasm polymorphic/unreachable region leaves behind. */
+ * nesting so the matching end pops the right depth. `base` is the CG
+ * value-stack depth just below the block's result region, used to discard the
+ * operands a Wasm polymorphic/unreachable region leaves behind. */
typedef struct WasmCgControl {
- uint8_t kind; /* WASM_INSN_BLOCK / LOOP / IF */
- uint8_t seen_else; /* an else has been processed (if frames) */
- uint8_t entry_dead; /* opened in dead code => no CG scope */
+ uint8_t kind; /* WASM_INSN_BLOCK / LOOP / IF */
+ uint8_t seen_else; /* an else has been processed (if frames) */
+ uint8_t entry_dead; /* opened in dead code => no CG scope */
uint8_t pad;
KitCgScope scope; /* valid iff !entry_dead */
uint32_t base; /* CG value-stack depth below the block's results */
@@ -2038,7 +2066,7 @@ typedef struct WasmCgControl {
/* Resolve a block/loop/if instruction's signature into CG-type arrays stored on
* `fr` (arena-owned). */
-static void wasm_cg_resolve_sig(KitCompiler* c, KitCgBuiltinTypes b,
+static void wasm_cg_resolve_sig(KitCompiler* c, WasmCgBuiltinTypes b,
const WasmModule* m, KitArena* arena,
const WasmInsn* in, WasmCgControl* fr) {
WasmValType scratch;
@@ -2085,7 +2113,8 @@ static KitCgLocal* wasm_cg_save_values(KitCg* cg, KitArena* arena,
attrs.flags = KIT_CG_LOCAL_COMPILER_TEMP;
for (k = 0; k < n; ++k) tmps[k] = kit_cg_local(cg, types[k], attrs);
for (k = 0; k < n; ++k)
- kit_cg_local_write(cg, tmps[n - 1u - k], wasm_cg_mem_type(types[n - 1u - k]));
+ kit_cg_local_write(cg, tmps[n - 1u - k],
+ wasm_cg_mem_type(types[n - 1u - k]));
if (restore) wasm_cg_push_temps(cg, types, tmps, n);
return tmps;
}
@@ -2102,7 +2131,8 @@ static void wasm_cg_branch_to(KitCg* cg, WasmCgControl* fr) {
}
}
-/* The label values a branch to `fr` transfers (loop -> params, else results). */
+/* The label values a branch to `fr` transfers (loop -> params, else results).
+ */
static const KitCgTypeId* wasm_cg_label_types(const WasmCgControl* fr,
uint32_t* n) {
if (fr->kind == WASM_INSN_LOOP) {
@@ -2129,9 +2159,10 @@ static void wasm_cg_enter_else(KitCg* cg, WasmCgControl* fr, int then_dead) {
* (just nesting); otherwise it opens the structured scope, and an `if` also
* lifts the condition, snapshots its params for the else arm, and branches the
* false arm to a fresh else label. */
-static void wasm_cg_open_frame(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
+static void wasm_cg_open_frame(KitCompiler* c, KitCg* cg, WasmCgBuiltinTypes b,
const WasmModule* m, KitArena* arena,
- WasmCgControl* fr, const WasmInsn* in, int dead) {
+ WasmCgControl* fr, const WasmInsn* in,
+ int dead) {
KitCgScopeSig sig;
memset(fr, 0, sizeof *fr);
fr->kind = in->kind;
@@ -2158,7 +2189,8 @@ static void wasm_cg_open_frame(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
/* Lift the condition above the params, snapshot the params for the else
* arm, then re-test the condition once the block scope is open. */
kit_cg_local_write(cg, cond_tmp, i32_mem);
- fr->param_tmps = wasm_cg_save_values(cg, arena, fr->params, fr->nparams, 1);
+ fr->param_tmps =
+ wasm_cg_save_values(cg, arena, fr->params, fr->nparams, 1);
fr->scope = kit_cg_block_begin_sig(cg, &sig);
kit_cg_local_read(cg, cond_tmp, i32_mem);
kit_cg_branch_false(cg, fr->else_label);
@@ -2171,7 +2203,8 @@ static void wasm_cg_open_frame(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
fr->base = kit_cg_stack_depth(cg) - fr->nparams;
}
-/* Process an `else`: returns the new dead state (the else arm's reachability). */
+/* Process an `else`: returns the new dead state (the else arm's reachability).
+ */
static int wasm_cg_handle_else(KitCg* cg, WasmCgControl* fr, int dead) {
if (fr->entry_dead) return dead; /* else of a dead if; stays dead */
wasm_cg_enter_else(cg, fr, dead);
@@ -2198,7 +2231,7 @@ static int wasm_cg_handle_end(KitCg* cg, WasmCgControl* fr, int dead) {
}
void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
- KitCgBuiltinTypes b = kit_cg_builtin_types(c);
+ WasmCgBuiltinTypes b = wasm_cg_builtin_types(c);
WasmCgRuntime rt;
/* A KitArena owns transient frontend-side codegen state — sym tables, func
* types, per-function local arrays, instance-record field tables, call
@@ -2219,6 +2252,7 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
KitCgFuncSig sig;
KitCgDecl decl;
memset(&sig, 0, sizeof sig);
+ sig.result.type = b.id[KIT_CG_BUILTIN_VOID];
sig.call_conv = KIT_CG_CC_TARGET_C;
rt.trap_func_ty = kit_cg_type_func(c, sig);
if (!rt.trap_func_ty)
@@ -2244,6 +2278,7 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
memset(&init_param, 0, sizeof init_param);
init_param.type = rt.instance_ptr_ty;
memset(&sig, 0, sizeof sig);
+ sig.result.type = b.id[KIT_CG_BUILTIN_VOID];
sig.params = &init_param;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -2270,6 +2305,7 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
cg_params[j + 1u].type = wasm_cg_type(c, b, f->params[j]);
}
memset(&sig, 0, sizeof sig);
+ sig.result.type = b.id[KIT_CG_BUILTIN_VOID];
if (f->nresults) sig.result.type = wasm_cg_type(c, b, f->results[0]);
sig.params = cg_params;
sig.nparams = f->nparams + 1u;
@@ -2298,7 +2334,7 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
}
for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) {
kit_cg_func_begin(cg, rt.trap_syms[k]);
- wasm_cg_emit_raw_trap(cg);
+ wasm_cg_emit_raw_trap(c, cg);
kit_cg_func_end(cg);
}
if (init_sym) {
@@ -2710,7 +2746,8 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
sw.ncases = in.ntargets - 1u;
sw.default_label = per_target[in.ntargets - 1u];
/* br_table is dense-by-construction (case values 0..N-1); hint a
- * jump table. Targets that ignore the hint fall back to a cmp chain. */
+ * jump table. Targets that ignore the hint fall back to a cmp chain.
+ */
sw.hint = KIT_CG_SWITCH_JUMP_TABLE;
kit_cg_switch(cg, sw);
for (q = 0; q < ndistinct; ++q) {
@@ -2824,6 +2861,7 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
for (uint32_t p = 0; p < t->nparams; ++p)
indirect_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
memset(&indirect_sig, 0, sizeof indirect_sig);
+ indirect_sig.result.type = b.id[KIT_CG_BUILTIN_VOID];
if (t->nresults)
indirect_sig.result.type = wasm_cg_type(c, b, t->results[0]);
indirect_sig.params = indirect_params;
@@ -2996,6 +3034,7 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
for (uint32_t p = 0; p < t->nparams; ++p)
ref_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
memset(&ref_sig, 0, sizeof ref_sig);
+ ref_sig.result.type = b.id[KIT_CG_BUILTIN_VOID];
if (t->nresults)
ref_sig.result.type = wasm_cg_type(c, b, t->results[0]);
ref_sig.params = ref_params;
diff --git a/mk/test.mk b/mk/test.mk
@@ -495,6 +495,7 @@ CG_API_TEST_BIN = build/test/cg_api_test
CG_SWITCH_TEST_BIN = build/test/cg_switch_test
CG_FP_CMP_TEST_BIN = build/test/cg_fp_cmp_test
CG_CONTROL_TEST_BIN = build/test/cg_control_test
+CG_CONST_TEST_BIN = build/test/cg_const_test
STRENGTH_REDUCE_TEST_BIN = build/test/strength_reduce_test
TARGET_TEST_BIN = build/test/target_test
HASH_TEST_BIN = build/test/hash_test
@@ -505,6 +506,7 @@ NATIVE_DIRECT_TARGET_TEST_BIN = build/test/native_direct_target_test
test-cg-api: $(TARGET_TEST_BIN) $(CG_API_TEST_BIN) $(CG_SWITCH_TEST_BIN) \
$(CG_FP_CMP_TEST_BIN) $(CG_CONTROL_TEST_BIN) \
+ $(CG_CONST_TEST_BIN) \
$(STRENGTH_REDUCE_TEST_BIN) \
$(PANIC_RECOVERY_TEST_BIN)
$(TARGET_TEST_BIN)
@@ -512,6 +514,7 @@ test-cg-api: $(TARGET_TEST_BIN) $(CG_API_TEST_BIN) $(CG_SWITCH_TEST_BIN) \
$(CG_SWITCH_TEST_BIN)
$(CG_FP_CMP_TEST_BIN)
$(CG_CONTROL_TEST_BIN)
+ $(CG_CONST_TEST_BIN)
$(STRENGTH_REDUCE_TEST_BIN)
$(PANIC_RECOVERY_TEST_BIN)
diff --git a/mk/test_unit.mk b/mk/test_unit.mk
@@ -31,7 +31,7 @@ UNIT_CFLAGS_INTERNAL = $(HOST_CFLAGS) -Iinclude -Isrc -Itest
UNIT_TESTS_PUBLIC := \
ar_test target_test cg_api_test cg_switch_test cg_fp_cmp_test \
- cg_control_test hash_test \
+ cg_control_test cg_const_test hash_test \
panic_recovery_test profile_test \
rv64_jit_test rv32_jit_test aa64_inline_test rv64_inline_test x64_inline_test \
strength_reduce_test
@@ -44,6 +44,7 @@ cg_api_test_SRC := test/api/cg_type_test.c
cg_switch_test_SRC := test/api/cg_switch_test.c
cg_fp_cmp_test_SRC := test/api/cg_fp_cmp_test.c
cg_control_test_SRC := test/api/cg_control_test.c
+cg_const_test_SRC := test/api/cg_const_test.c
strength_reduce_test_SRC := test/cg/strength_reduce_test.c
rv64_jit_test_SRC := test/link/rv64_jit_test.c
rv32_jit_test_SRC := test/link/rv32_jit_test.c
diff --git a/src/abi/abi.c b/src/abi/abi.c
@@ -124,7 +124,9 @@ static ABIRecordLayout* compute_record_layout(TargetABI* a, KitCgTypeId id) {
ABIRecordLayout* L = arena_new(a->c->tu, ABIRecordLayout);
const CgType* t = cg_type_get(a->c, id);
if (!L) return NULL;
- if (!t || t->kind != KIT_CG_TYPE_RECORD) return NULL;
+ if (!t || t->kind != KIT_CG_TYPE_RECORD ||
+ !(t->record.flags & CG_TYPE_RECORD_COMPLETE))
+ return NULL;
memset(L, 0, sizeof *L);
ABIFieldLayout* fl = NULL;
if (t->record.nfields) {
@@ -151,7 +153,9 @@ const ABIRecordLayout* abi_cg_record_layout(TargetABI* a, KitCgTypeId id) {
const CgType* t = cg_type_get(a->c, id);
ABIRecordLayout** hit;
ABIRecordLayout* L;
- if (!t || t->kind != KIT_CG_TYPE_RECORD) return NULL;
+ if (!t || t->kind != KIT_CG_TYPE_RECORD ||
+ !(t->record.flags & CG_TYPE_RECORD_COMPLETE))
+ return NULL;
hit = AbiRecLayoutMap_get(&a->rec_cache, id);
if (hit) return *hit;
L = compute_record_layout(a, id);
diff --git a/src/arch/c_target/c_emit.c b/src/arch/c_target/c_emit.c
@@ -2297,9 +2297,12 @@ void c_emit_ret(CTarget* t, CGLocal value) {
/* CG emits a defensive void-return epilogue at the end of every function. For
* a non-void function that's unreachable; emitting a bare `return;` would
* trip -Wreturn-type. Spell it as `__builtin_unreachable()` so the host C
- * compiler sees the path is dead without us inventing a fake value. */
+ * compiler sees the path is dead without us inventing a fake value. A genuine
+ * void return (the function's result is the void builtin) must still emit
+ * `return;` -- testing against KIT_CG_TYPE_NONE here would misfire, since the
+ * cutover represents void as the void builtin, not NONE. */
if (value == CG_LOCAL_NONE && t->cur_fn) {
- if (t->cur_fn->result_type != KIT_CG_TYPE_NONE) {
+ if (!cg_type_is_void(t->c, t->cur_fn->result_type)) {
cbuf_puts(&t->body, " __builtin_unreachable();\n");
t->last_was_terminator = 1;
return;
diff --git a/src/arch/riscv/emu.c b/src/arch/riscv/emu.c
@@ -74,12 +74,11 @@ static KitCgTypeId rv64_emu_func_type(KitCompiler* c, KitCgTypeId ret,
static void rv64_emu_lift_syms_init(KitCompiler* c, KitCg* cg,
Rv64EmuLiftSyms* out) {
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
KitCgTypeId params[5];
memset(out, 0, sizeof(*out));
- out->void_ty = bi.id[KIT_CG_BUILTIN_VOID];
- out->i32 = bi.id[KIT_CG_BUILTIN_I32];
- out->i64 = bi.id[KIT_CG_BUILTIN_I64];
+ out->void_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
+ out->i32 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
+ out->i64 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
out->i64_ptr = kit_cg_type_ptr(c, out->i64, 0);
out->thread_ptr = emu_thread_type((Compiler*)c);
diff --git a/src/cg/arith.c b/src/cg/arith.c
@@ -28,6 +28,7 @@ static int api_try_fold_int_convert(KitCg* g, ConvKind ck, KitCgTypeId sty,
void api_cg_binop(KitCg* g, BinOp iop, u32 flags) {
ApiSValue b, a;
+ ApiConstValue cb, ca, result_const;
CgTarget* T;
KitCgTypeId ty;
Operand ra, rb;
@@ -37,10 +38,20 @@ void api_cg_binop(KitCg* g, BinOp iop, u32 flags) {
i64 folded;
int can_delay;
if (!g) return;
+ cb = api_const_at(g, 0);
+ ca = api_const_at(g, 1);
T = g->target;
b = api_pop(g);
a = api_pop(g);
ty = a.type ? a.type : b.type;
+ api_const_fold_binop(g, iop, ty, ca, cb, flags, &result_const);
+ if (api_unevaluated(g)) {
+ api_release(g, &a);
+ api_release(g, &b);
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, result_const);
+ return;
+ }
/* Delayability is a pure function of (ty, flags), neither of which changes
* below (strength-reduce rewrites the op and operands, not the type), so
* classify the foldable int once instead of re-deriving it at each of the
@@ -52,6 +63,7 @@ void api_cg_binop(KitCg* g, BinOp iop, u32 flags) {
api_release(g, &a);
api_release(g, &b);
api_push(g, api_make_sv(api_op_imm(folded, ty), ty));
+ api_const_set_top(g, result_const);
return;
}
@@ -60,11 +72,11 @@ void api_cg_binop(KitCg* g, BinOp iop, u32 flags) {
* identity / fallback machinery as any other shift or and. */
if (!flags) api_try_strength_reduce(g, &iop, ty, &a, &b);
- if (can_delay &&
- api_try_fold_arith_chain(g, iop, ty, &a, &b, &folded_sv)) {
+ if (can_delay && api_try_fold_arith_chain(g, iop, ty, &a, &b, &folded_sv)) {
api_release(g, &a);
api_release(g, &b);
api_push(g, folded_sv);
+ api_const_set_top(g, result_const);
return;
}
@@ -81,20 +93,21 @@ void api_cg_binop(KitCg* g, BinOp iop, u32 flags) {
api_release(g, &a);
api_release(g, &b);
api_push(g, folded_sv);
+ api_const_set_top(g, result_const);
return;
}
- if (can_delay &&
- (ra.kind == OPK_LOCAL || rb.kind == OPK_LOCAL) &&
+ if (can_delay && (ra.kind == OPK_LOCAL || rb.kind == OPK_LOCAL) &&
(ra.kind == OPK_LOCAL || ra.kind == OPK_IMM) &&
(rb.kind == OPK_LOCAL || rb.kind == OPK_IMM)) {
int a_owned = api_sv_owns_operand_local(&a, &ra);
int b_owned = api_sv_owns_operand_local(&b, &rb);
api_push(g, api_make_arith_binop(g, iop, ra, rb, ty, a_owned, b_owned));
- if (a_owned) a.res = RES_INHERENT;
- if (b_owned) b.res = RES_INHERENT;
+ if (a_owned) api_sv_set_res(&a, RES_INHERENT);
+ if (b_owned) api_sv_set_res(&b, RES_INHERENT);
api_release(g, &a);
api_release(g, &b);
+ api_const_set_top(g, result_const);
return;
}
@@ -108,10 +121,12 @@ void api_cg_binop(KitCg* g, BinOp iop, u32 flags) {
api_release(g, &a);
api_release(g, &b);
api_push(g, api_make_sv(dst, ty));
+ api_const_set_top(g, result_const);
}
void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
ApiSValue a;
+ ApiConstValue ca, result_const;
CgTarget* T;
KitCgTypeId ty;
Operand ra;
@@ -121,9 +136,17 @@ void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
i64 folded;
int can_delay;
if (!g) return;
+ ca = api_const_at(g, 0);
T = g->target;
a = api_pop(g);
ty = a.type ? a.type : a.op.type;
+ api_const_fold_unop(g, iop, ty, ca, flags, &result_const);
+ if (api_unevaluated(g)) {
+ api_release(g, &a);
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, result_const);
+ return;
+ }
/* Pure function of (ty, flags); classify the foldable int once for both delay
* gates below. */
can_delay = api_can_delay_int_arith(g, ty, flags);
@@ -140,6 +163,7 @@ void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
T->unop(T, iop, dst, ra);
api_release(g, &a);
api_push(g, api_make_sv(dst, ty));
+ api_const_set_top(g, result_const);
return;
}
@@ -147,9 +171,10 @@ void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
* place. For FP this flips ordered<->unordered as well as the relation (via
* api_invert_cmp), so `!(a<b)` becomes UGE (NaN -> true), matching IEEE
* negation. The inverted compare keeps the same i32 result type. */
- if (iop == UO_NOT && a.kind == SV_CMP) {
+ if (iop == UO_NOT && api_sv_kind(&a) == SV_CMP) {
a.delayed->cmp.op = api_invert_cmp(a.delayed->cmp.op);
api_push(g, a);
+ api_const_set_top(g, result_const);
return;
}
@@ -157,12 +182,14 @@ void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
api_try_fold_int_unop(g, iop, ty, a.op.v.imm, &folded)) {
api_release(g, &a);
api_push(g, api_make_sv(api_op_imm(folded, ty), ty));
+ api_const_set_top(g, result_const);
return;
}
if (can_delay && api_try_fold_unary_chain(&a, iop, ty, &folded_sv)) {
api_release(g, &a);
api_push(g, folded_sv);
+ api_const_set_top(g, result_const);
return;
}
@@ -170,8 +197,9 @@ void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
if (can_delay && ra.kind == OPK_LOCAL) {
int a_owned = api_sv_owns_operand_local(&a, &ra);
api_push(g, api_make_arith_unop(g, iop, ra, ty, a_owned));
- if (a_owned) a.res = RES_INHERENT;
+ if (a_owned) api_sv_set_res(&a, RES_INHERENT);
api_release(g, &a);
+ api_const_set_top(g, result_const);
return;
}
rr = api_alloc_temp_local(g, ty);
@@ -180,25 +208,38 @@ void api_cg_unop(KitCg* g, UnOp iop, u32 flags) {
T->unop(T, iop, dst, ra);
api_release(g, &a);
api_push(g, api_make_sv(dst, ty));
+ api_const_set_top(g, result_const);
}
void api_cg_cmp(KitCg* g, CmpOp cop) {
ApiSValue b, a;
+ ApiConstValue cb, ca, result_const;
KitCgTypeId opty;
KitCgTypeId i32;
Operand ra, rb;
i64 folded;
if (!g) return;
+ cb = api_const_at(g, 0);
+ ca = api_const_at(g, 1);
b = api_pop(g);
a = api_pop(g);
opty = a.type ? a.type : b.type;
i32 = builtin_id(KIT_CG_BUILTIN_I32);
+ api_const_fold_cmp(g, cop, ca, cb, &result_const);
+ if (api_unevaluated(g)) {
+ api_release(g, &a);
+ api_release(g, &b);
+ api_push(g, api_uneval_value(g, i32));
+ api_const_set_top(g, result_const);
+ return;
+ }
if (api_sv_op_is(&a, OPK_IMM) && api_sv_op_is(&b, OPK_IMM) &&
api_try_fold_int_cmp(g, cop, opty, a.op.v.imm, b.op.v.imm, &folded)) {
api_release(g, &a);
api_release(g, &b);
api_push(g, api_make_sv(api_op_imm(folded, i32), i32));
+ api_const_set_top(g, result_const);
return;
}
@@ -212,9 +253,9 @@ void api_cg_cmp(KitCg* g, CmpOp cop) {
* unchanged via api_materialize_cmp_to, which calls T->cmp with the same
* opcode the eager path used to. */
api_push(g,
- api_make_cmp(g, cop, ra, rb, i32,
- api_sv_owns_operand_local(&a, &ra),
+ api_make_cmp(g, cop, ra, rb, i32, api_sv_owns_operand_local(&a, &ra),
api_sv_owns_operand_local(&b, &rb)));
+ api_const_set_top(g, result_const);
}
int api_try_i128_convert(KitCg* g, ConvKind ck, KitCgTypeId sty,
@@ -224,6 +265,7 @@ int api_try_wide8_convert(KitCg* g, ConvKind ck, KitCgTypeId sty,
void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
ApiSValue v;
+ ApiConstValue in_const, result_const;
CgTarget* T;
KitCgTypeId sty;
KitCgTypeId dty;
@@ -237,16 +279,25 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
* return-before-pop order on an invalid dst_type. */
dty = api_unalias_type(g->c, dst_type);
if (!dty) return;
+ in_const = api_const_at(g, 0);
v = api_pop(g);
sty = api_unalias_type(g->c, v.type ? v.type : v.op.type);
if (!sty) {
api_release(g, &v);
return;
}
+ api_const_fold_convert(g, ck, sty, dty, in_const, &result_const);
+ if (api_unevaluated(g)) {
+ api_release(g, &v);
+ api_push(g, api_uneval_value(g, dty));
+ api_const_set_top(g, result_const);
+ return;
+ }
if (sty == dty) {
v.type = dty;
v.op.type = dty;
api_push(g, v);
+ api_const_set_top(g, result_const);
return;
}
if (api_sv_op_is(&v, OPK_IMM)) {
@@ -259,11 +310,18 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
api_push(g, api_make_wide8_int_const(g, folded, dty));
else
api_push(g, api_make_sv(api_op_imm(folded, dty), dty));
+ api_const_set_top(g, result_const);
return;
}
}
- if (api_try_i128_convert(g, ck, sty, dty, &v)) return;
- if (api_try_wide8_convert(g, ck, sty, dty, &v)) return;
+ if (api_try_i128_convert(g, ck, sty, dty, &v)) {
+ api_const_set_top(g, result_const);
+ return;
+ }
+ if (api_try_wide8_convert(g, ck, sty, dty, &v)) {
+ api_const_set_top(g, result_const);
+ return;
+ }
/* A bitcast between two narrow types of equal width and register class —
* pointer<->pointer-width integer (the frontend's ptr_to_int/int_to_ptr that
* brackets every field-offset add) or pointer<->pointer — reinterprets the
@@ -292,6 +350,7 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
v.type = dty;
v.op.type = dty;
api_push(g, v);
+ api_const_set_top(g, result_const);
return;
}
}
@@ -307,12 +366,9 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
Operand dst_addr;
Operand src_addr;
AggregateAccess agg;
- src_lv.lvalue = 1;
- dst_addr = api_lvalue_addr(
- g,
- &(ApiSValue){
- .op = dst_lv, .type = dty, .kind = SV_OPERAND, .lvalue = 1},
- ptr_ty);
+ ApiSValue dst_place = api_make_lv(dst_lv, dty);
+ api_sv_set_lvalue(&src_lv, 1);
+ dst_addr = api_lvalue_addr(g, &dst_place, ptr_ty);
src_addr = api_lvalue_addr(g, &src_lv, cg_type_ptr_to(g->c, sty));
memset(&agg, 0, sizeof agg);
agg.size = 16;
@@ -336,6 +392,7 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
}
api_release(g, &v);
api_push(g, api_make_lv(dst_lv, dty));
+ api_const_set_top(g, result_const);
return;
}
@@ -352,6 +409,7 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
T->convert(T, ck, dst, src);
api_release(g, &v);
api_push(g, api_make_sv(dst, dty));
+ api_const_set_top(g, result_const);
}
/* ============================================================
@@ -1122,8 +1180,23 @@ int api_try_i128_convert(KitCg* g, ConvKind ck, KitCgTypeId sty,
void kit_cg_int_binop(KitCg* g, KitCgIntBinOp op, uint32_t flags) {
BinOp iop = api_map_int_binop(op);
+ ApiConstValue result_const;
+ KitCgTypeId ty = KIT_CG_TYPE_NONE;
+ if (g && g->sp >= 2u) {
+ ApiConstValue cb = api_const_at(g, 0);
+ ApiConstValue ca = api_const_at(g, 1);
+ ty = api_sv_type(&g->stack[g->sp - 2u]);
+ api_const_fold_binop(g, iop, ty, ca, cb, flags, &result_const);
+ } else {
+ result_const = api_const_unknown(KIT_CG_TYPE_NONE);
+ }
+ if (api_unevaluated(g)) {
+ api_cg_binop(g, iop, flags);
+ return;
+ }
if (g && (api_i128_stack_top(g, 0) || api_i128_stack_top(g, 1))) {
api_i128_binop(g, iop);
+ api_const_set_top(g, result_const);
return;
}
/* 64-bit int split into 32-bit lanes: mul/div/rem/shift become __*di3
@@ -1136,6 +1209,7 @@ void kit_cg_int_binop(KitCg* g, KitCgIntBinOp op, uint32_t flags) {
api_wideint64_binop(g, iop);
else
api_wide64_binop_inline(g, iop);
+ api_const_set_top(g, result_const);
return;
}
api_cg_binop(g, iop, flags);
@@ -1143,8 +1217,22 @@ void kit_cg_int_binop(KitCg* g, KitCgIntBinOp op, uint32_t flags) {
void kit_cg_int_unop(KitCg* g, KitCgIntUnOp op, uint32_t flags) {
UnOp iop = api_map_int_unop(op);
+ ApiConstValue result_const;
+ KitCgTypeId ty = KIT_CG_TYPE_NONE;
+ if (g && g->sp >= 1u) {
+ ApiConstValue ca = api_const_at(g, 0);
+ ty = api_sv_type(&g->stack[g->sp - 1u]);
+ api_const_fold_unop(g, iop, ty, ca, flags, &result_const);
+ } else {
+ result_const = api_const_unknown(KIT_CG_TYPE_NONE);
+ }
+ if (api_unevaluated(g)) {
+ api_cg_unop(g, iop, flags);
+ return;
+ }
if (g && api_i128_stack_top(g, 0) && (iop == UO_NEG || iop == UO_BNOT)) {
api_i128_unop(g, iop);
+ api_const_set_top(g, result_const);
return;
}
/* Split 64-bit int: neg/bnot are inline 2-word lane ops; logical-not (!x) is
@@ -1152,6 +1240,7 @@ void kit_cg_int_unop(KitCg* g, KitCgIntUnOp op, uint32_t flags) {
if (g && api_wide64_stack_top(g, 0)) {
if (iop == UO_NEG || iop == UO_BNOT) {
api_wide64_unop_inline(g, iop);
+ api_const_set_top(g, result_const);
return;
}
if (iop == UO_NOT) {
@@ -1164,6 +1253,7 @@ void kit_cg_int_unop(KitCg* g, KitCgIntUnOp op, uint32_t flags) {
kit_cg_push_int(g, 0, i32);
api_cg_cmp(g, CMP_EQ);
api_cg_convert_kind(g, ty, CV_ZEXT);
+ api_const_set_top(g, result_const);
return;
}
}
@@ -1172,12 +1262,26 @@ void kit_cg_int_unop(KitCg* g, KitCgIntUnOp op, uint32_t flags) {
void kit_cg_int_cmp(KitCg* g, KitCgIntCmpOp op) {
CmpOp cop = api_map_int_cmp(op);
+ ApiConstValue result_const;
+ if (g && g->sp >= 2u) {
+ ApiConstValue cb = api_const_at(g, 0);
+ ApiConstValue ca = api_const_at(g, 1);
+ api_const_fold_cmp(g, cop, ca, cb, &result_const);
+ } else {
+ result_const = api_const_unknown(builtin_id(KIT_CG_BUILTIN_I32));
+ }
+ if (api_unevaluated(g)) {
+ api_cg_cmp(g, cop);
+ return;
+ }
if (g && (api_i128_stack_top(g, 0) || api_i128_stack_top(g, 1))) {
api_i128_cmp(g, cop);
+ api_const_set_top(g, result_const);
return;
}
if (g && (api_wide64_stack_top(g, 0) || api_wide64_stack_top(g, 1))) {
api_wide64_cmp_inline(g, cop);
+ api_const_set_top(g, result_const);
return;
}
api_cg_cmp(g, cop);
@@ -1339,6 +1443,10 @@ void api_f128_call_unary(KitCg* g, const char* name, KitCgTypeId ret,
void kit_cg_fp_binop(KitCg* g, KitCgFpBinOp op, uint32_t flags) {
(void)flags;
+ if (api_unevaluated(g)) {
+ api_cg_binop(g, api_map_fp_binop(op), 0);
+ return;
+ }
if (api_f128_stack_top(g, 0) || api_f128_stack_top(g, 1)) {
api_softfp_binop(g, api_f128_binop_helper(op),
builtin_id(KIT_CG_BUILTIN_F128), "f128");
@@ -1363,6 +1471,10 @@ void kit_cg_fp_unop(KitCg* g, KitCgFpUnOp op, uint32_t flags) {
if (op != KIT_CG_FP_NEG) {
compiler_panic(g->c, g->cur_loc, "KitCg: FP unary op unsupported");
}
+ if (api_unevaluated(g)) {
+ api_cg_unop(g, UO_FNEG, 0);
+ return;
+ }
if (api_f128_stack_top(g, 0)) {
KitCgTypeId f128 = builtin_id(KIT_CG_BUILTIN_F128);
api_f128_call_unary(g, "__negtf2", f128, f128);
@@ -1502,6 +1614,10 @@ void kit_cg_fp_cmp(KitCg* g, KitCgFpCmpOp op) {
* must split "equal" from "unordered", need a second (__unord*2) call. The
* convention is width-neutral, so the same logic drives the tf and df
* suffixes via api_softfp_cmp. */
+ if (api_unevaluated(g)) {
+ api_cg_cmp(g, api_map_fp_cmp(op));
+ return;
+ }
if (api_f128_stack_top(g, 0) || api_f128_stack_top(g, 1)) {
api_softfp_cmp(g, op, "tf", builtin_id(KIT_CG_BUILTIN_F128));
return;
@@ -1547,6 +1663,11 @@ void kit_cg_fpext(KitCg* g, KitCgTypeId dst) {
* redundant: for a valid id resolve_type(dst)==dst, for an invalid id every
* predicate is false and the fall-through validates. */
KitCgTypeId dty = dst;
+ if (!g) return;
+ if (api_unevaluated(g)) {
+ api_cg_convert_kind(g, dst, CV_FEXT);
+ return;
+ }
if (api_is_f128_type(g->c, dty)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
@@ -1569,8 +1690,14 @@ void kit_cg_fpext(KitCg* g, KitCgTypeId dst) {
void kit_cg_fptrunc(KitCg* g, KitCgTypeId dst) {
/* resolve_type(dst)==dst for any valid id; the predicates / fall-through
- * validate, so drop the redundant standalone validation (see kit_cg_fpext). */
+ * validate, so drop the redundant standalone validation (see kit_cg_fpext).
+ */
KitCgTypeId dty = dst;
+ if (!g) return;
+ if (api_unevaluated(g)) {
+ api_cg_convert_kind(g, dst, CV_FTRUNC);
+ return;
+ }
if (api_f128_stack_top(g, 0)) {
ApiSValue v = api_pop(g);
KitCgTypeId f128 = builtin_id(KIT_CG_BUILTIN_F128);
@@ -1645,6 +1772,10 @@ static void api_fp_conv_name(char* buf, size_t cap, ApiFpConvOp op,
void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
+ if (api_unevaluated(g)) {
+ api_cg_convert_kind(g, dst, CV_ITOF_S);
+ return;
+ }
if (api_is_f128_type(g->c, dst)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
@@ -1652,8 +1783,7 @@ void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_SINT_TO_FLOAT, "tf", sz);
api_push(g, v);
- api_f128_call_unary(g, name, dst,
- api_int_builtin_for_size(sz));
+ api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz));
return;
}
/* signed int -> soft double: __floatsidf (i32) / __floatdidf (i64). */
@@ -1664,20 +1794,17 @@ void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_SINT_TO_FLOAT, "df", sz);
api_push(g, v);
- api_f128_call_unary(g, name, dst,
- api_int_builtin_for_size(sz));
+ api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz));
return;
}
/* signed split-i64 -> hardware single float: use __floatdisf. */
if (api_wide64_stack_top(g, 0)) {
- api_f128_call_unary(g, "__floatdisf", dst,
- builtin_id(KIT_CG_BUILTIN_I64));
+ api_f128_call_unary(g, "__floatdisf", dst, builtin_id(KIT_CG_BUILTIN_I64));
return;
}
/* i32 -> soft single float (ilp32, no FPU): __floatsisf. */
if (api_type_is_soft_single(g, dst)) {
- api_f128_call_unary(g, "__floatsisf", dst,
- builtin_id(KIT_CG_BUILTIN_I32));
+ api_f128_call_unary(g, "__floatsisf", dst, builtin_id(KIT_CG_BUILTIN_I32));
return;
}
api_cg_convert_kind(g, dst, CV_ITOF_S);
@@ -1685,6 +1812,10 @@ void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
+ if (api_unevaluated(g)) {
+ api_cg_convert_kind(g, dst, CV_ITOF_U);
+ return;
+ }
if (api_is_f128_type(g->c, dst)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
@@ -1692,8 +1823,7 @@ void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_UINT_TO_FLOAT, "tf", sz);
api_push(g, v);
- api_f128_call_unary(g, name, dst,
- api_int_builtin_for_size(sz));
+ api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz));
return;
}
/* unsigned int -> soft double: __floatunsidf (i32) / __floatundidf (i64). */
@@ -1704,8 +1834,7 @@ void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_UINT_TO_FLOAT, "df", sz);
api_push(g, v);
- api_f128_call_unary(g, name, dst,
- api_int_builtin_for_size(sz));
+ api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz));
return;
}
/* unsigned i64 -> hardware single float: __floatundisf. */
@@ -1725,6 +1854,10 @@ void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
+ if (api_unevaluated(g)) {
+ api_cg_convert_kind(g, dst, CV_FTOI_S);
+ return;
+ }
if (api_f128_stack_top(g, 0)) {
KitCgTypeId dty = dst;
u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty);
@@ -1749,8 +1882,7 @@ void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
}
/* hardware single float -> split-i64: use __fixsfdi. */
if (api_is_wide8_scalar_type(g->c, dst)) {
- api_f128_call_unary(g, "__fixsfdi", dst,
- builtin_id(KIT_CG_BUILTIN_F32));
+ api_f128_call_unary(g, "__fixsfdi", dst, builtin_id(KIT_CG_BUILTIN_F32));
return;
}
/* soft single float -> signed int <=32 (ilp32, no FPU): __fixsfsi. */
@@ -1766,6 +1898,10 @@ void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
void kit_cg_float_to_uint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
+ if (api_unevaluated(g)) {
+ api_cg_convert_kind(g, dst, CV_FTOI_U);
+ return;
+ }
if (api_f128_stack_top(g, 0)) {
KitCgTypeId dty = dst;
u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty);
@@ -1790,8 +1926,7 @@ void kit_cg_float_to_uint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
}
/* hardware single float -> split-u64: use __fixunssfdi. */
if (api_is_wide8_scalar_type(g->c, dst)) {
- api_f128_call_unary(g, "__fixunssfdi", dst,
- builtin_id(KIT_CG_BUILTIN_F32));
+ api_f128_call_unary(g, "__fixunssfdi", dst, builtin_id(KIT_CG_BUILTIN_F32));
return;
}
/* soft single float -> unsigned int <=32 (ilp32, no FPU): __fixunssfsi. */
@@ -1926,6 +2061,26 @@ void kit_cg_intrinsic(KitCg* g, KitCgIntrinsic intrin, uint32_t nargs,
u32 ndst = 0;
Heap* h;
if (!g) return;
+ if (api_unevaluated(g)) {
+ KitCgTypeId rty_u = resolve_type(g->c, result_type);
+ for (u32 i = 0; i < nargs; ++i) {
+ ApiSValue sv = api_pop(g);
+ api_release(g, &sv);
+ }
+ if (api_intrinsic_is_overflow(intrin)) {
+ KitCgTypeId bool_ty = builtin_id(KIT_CG_BUILTIN_BOOL);
+ if (!rty_u) rty_u = builtin_id(KIT_CG_BUILTIN_I32);
+ api_push(g, api_uneval_value(g, rty_u));
+ api_const_set_top(g, api_const_unknown(rty_u));
+ api_push(g, api_uneval_value(g, bool_ty));
+ api_const_set_top(g, api_const_unknown(bool_ty));
+ } else if (!api_intrinsic_is_void(intrin) &&
+ (rty_u && !cg_type_is_void(g->c, rty_u))) {
+ api_push(g, api_uneval_value(g, rty_u));
+ api_const_set_top(g, api_const_unknown(rty_u));
+ }
+ return;
+ }
/* readcyclecounter returns a 64-bit value. On rv32 that is a register pair
* the single-register native intrinsic path can't carry, and the read spans
* the cycle/cycleh CSRs with a re-read loop, so route to the libkit_rt helper
diff --git a/src/cg/asm.c b/src/cg/asm.c
@@ -101,6 +101,22 @@ void kit_cg_inline_asm(KitCg* g, KitCgInlineAsm asm_block) {
uint32_t clobber_abi_sets = asm_block.clobber_abi_sets;
(void)asm_block.flags;
if (!g) return;
+ if (api_unevaluated(g)) {
+ uint32_t ninout_u = 0;
+ for (u32 i = 0; i < noutputs; ++i)
+ if (outputs[i].dir == KIT_CG_ASM_INOUT) ninout_u++;
+ for (u32 i = 0; i < ninputs + ninout_u; ++i) {
+ ApiSValue sv = api_pop(g);
+ api_release(g, &sv);
+ }
+ for (u32 i = 0; i < noutputs; ++i) {
+ KitCgTypeId oty = resolve_type(g->c, outputs[i].type);
+ if (!oty) oty = builtin_id(KIT_CG_BUILTIN_I64);
+ api_push(g, api_uneval_value(g, oty));
+ api_const_set_top(g, api_const_unknown(oty));
+ }
+ return;
+ }
api_local_const_memory_boundary(g);
T = g->target;
h = g->c->ctx->heap;
@@ -253,7 +269,7 @@ void kit_cg_inline_asm(KitCg* g, KitCgInlineAsm asm_block) {
cg_type_ptr_to(g->c, ity ? ity : builtin_id(KIT_CG_BUILTIN_VOID));
Operand dst = api_lvalue_addr(g, &in_svs[i], pty);
in_svs[i].op = api_op_indirect(dst.v.local, 0, ity);
- in_svs[i].res = RES_LOCAL;
+ api_sv_set_res(&in_svs[i], RES_LOCAL);
in_ops[i] = in_svs[i].op;
} else {
compiler_panic(g->c, g->cur_loc,
@@ -297,7 +313,7 @@ void kit_cg_inline_asm(KitCg* g, KitCgInlineAsm asm_block) {
for (u32 i = 0; i < g->sp; ++i) {
ApiSValue* sv = &g->stack[i];
CGLocal local_id;
- if (sv->res != RES_LOCAL) continue;
+ if (api_sv_res(sv) != RES_LOCAL) continue;
local_id = api_local_of_sv(sv);
api_asm_memory_clobber_sv(g, sv, local_id);
}
@@ -310,7 +326,8 @@ void kit_cg_inline_asm(KitCg* g, KitCgInlineAsm asm_block) {
for (u32 i = 0; i < noutputs; ++i) {
KitCgTypeId oty = outs[i].type ? outs[i].type : fallback_ty;
ApiSValue sv = api_make_sv(out_ops[i], oty);
- if (!out_local_owned[i] && sv.res == RES_LOCAL) sv.res = RES_INHERENT;
+ if (!out_local_owned[i] && api_sv_res(&sv) == RES_LOCAL)
+ api_sv_set_res(&sv, RES_INHERENT);
api_push(g, sv);
}
@@ -325,6 +342,7 @@ void kit_cg_inline_asm(KitCg* g, KitCgInlineAsm asm_block) {
void kit_cg_file_scope_asm(KitCg* g, KitSlice asm_source) {
if (!g || !asm_source.s) return;
+ if (api_unevaluated(g)) return;
if (g->check_only) return;
if (g->target && g->target->file_scope_asm) {
g->target->file_scope_asm(g->target, asm_source.s, asm_source.len);
diff --git a/src/cg/atomic.c b/src/cg/atomic.c
@@ -104,7 +104,10 @@ static KitCgSym cg_atomic_runtime_sym(KitCg* g, const char* name,
for (u32 i = 0; i < nparams; ++i) ps[i].type = params[i];
memset(&sig, 0, sizeof sig);
memset(&result, 0, sizeof result);
- result.type = ret; /* ret == KIT_CG_TYPE_NONE -> void result */
+ /* A function result is always a valid type id post-cutover; a void helper
+ * (e.g. __atomic_store_8) is the void builtin, never KIT_CG_TYPE_NONE, which
+ * kit_cg_type_func rejects. Normalize a NONE/0 ret to the void builtin. */
+ result.type = ret ? ret : builtin_id(KIT_CG_BUILTIN_VOID);
sig.result = result;
sig.params = ps;
sig.nparams = nparams;
@@ -144,6 +147,12 @@ void kit_cg_atomic_load(KitCg* g, KitCgMemAccess access, KitCgMemOrder order) {
pty = api_sv_type(&ptr);
val_ty = resolve_type(g->c, access.type);
if (!val_ty) val_ty = api_atomic_pointee(g, pty, "KitCg: atomic_load");
+ if (api_unevaluated(g)) {
+ api_release(g, &ptr);
+ api_push(g, api_uneval_value(g, val_ty));
+ api_const_set_top(g, api_const_unknown(val_ty));
+ return;
+ }
api_require_pointer_value(g, "atomic_load pointer", pty);
if (cg_atomic_needs_libcall(g, val_ty)) {
/* u64 __atomic_load_8(const void* ptr, int memorder) */
@@ -177,6 +186,11 @@ void kit_cg_atomic_store(KitCg* g, KitCgMemAccess access, KitCgMemOrder order) {
pty = api_sv_type(&ptr);
val_ty = resolve_type(g->c, access.type);
if (!val_ty) val_ty = api_atomic_pointee(g, pty, "KitCg: atomic_store");
+ if (api_unevaluated(g)) {
+ api_release(g, &val);
+ api_release(g, &ptr);
+ return;
+ }
api_require_pointer_value(g, "atomic_store pointer", pty);
api_validate_memory_value(g, "atomic_store", val_ty, api_sv_type(&val));
if (cg_atomic_needs_libcall(g, val_ty)) {
@@ -215,6 +229,13 @@ void kit_cg_atomic_rmw(KitCg* g, KitCgMemAccess access, KitCgAtomicOp op,
pty = api_sv_type(&ptr);
val_ty = resolve_type(g->c, access.type);
if (!val_ty) val_ty = api_atomic_pointee(g, pty, "KitCg: atomic_rmw");
+ if (api_unevaluated(g)) {
+ api_release(g, &val);
+ api_release(g, &ptr);
+ api_push(g, api_uneval_value(g, val_ty));
+ api_const_set_top(g, api_const_unknown(val_ty));
+ return;
+ }
api_require_pointer_value(g, "atomic_rmw pointer", pty);
api_validate_memory_value(g, "atomic_rmw", val_ty, api_sv_type(&val));
if (cg_atomic_needs_libcall(g, val_ty)) {
@@ -266,6 +287,17 @@ void kit_cg_atomic_cmpxchg(KitCg* g, KitCgMemAccess access,
pty = api_sv_type(&ptr);
val_ty = resolve_type(g->c, access.type);
if (!val_ty) val_ty = api_atomic_pointee(g, pty, "KitCg: atomic_cmpxchg");
+ if (api_unevaluated(g)) {
+ bool_ty = builtin_id(KIT_CG_BUILTIN_BOOL);
+ api_release(g, &desired);
+ api_release(g, &expected);
+ api_release(g, &ptr);
+ api_push(g, api_uneval_value(g, val_ty));
+ api_const_set_top(g, api_const_unknown(val_ty));
+ api_push(g, api_uneval_value(g, bool_ty));
+ api_const_set_top(g, api_const_unknown(bool_ty));
+ return;
+ }
api_require_pointer_value(g, "atomic_cmpxchg pointer", pty);
api_validate_memory_value(g, "atomic_cmpxchg expected", val_ty,
api_sv_type(&expected));
@@ -347,6 +379,7 @@ void kit_cg_atomic_cmpxchg(KitCg* g, KitCgMemAccess access,
void kit_cg_atomic_fence(KitCg* g, KitCgMemOrder order) {
if (!g) return;
+ if (api_unevaluated(g)) return;
api_local_const_memory_boundary(g);
g->target->fence(g->target, order);
}
diff --git a/src/cg/call.c b/src/cg/call.c
@@ -97,16 +97,19 @@ void api_pack_call_arg(KitCg* g, CGLocal* out, KitCgTypeId fty, u32 idx) {
*
* Reversing the on-stack order (rather than popping all up front) preserves the
* api_temp_dead semantics exactly: the not-yet-materialized arguments stay on
- * the stack, so a temp shared between arguments (e.g. f(t*2, t*3)) is scanned as
- * live until its last (highest-index) use and is coalesced there, never killed
- * prematurely. */
+ * the stack, so a temp shared between arguments (e.g. f(t*2, t*3)) is scanned
+ * as live until its last (highest-index) use and is coalesced there, never
+ * killed prematurely. */
static void api_pack_call_args_in_order(KitCg* g, CGLocal* args, u32 nargs,
KitCgTypeId fty) {
u32 base = g->sp - nargs; /* arg0's stack slot (callee, if any, sits below) */
for (u32 i = 0; i < nargs / 2u; ++i) {
ApiSValue tmp = g->stack[base + i];
+ ApiConstValue ctmp = g->const_stack[base + i];
g->stack[base + i] = g->stack[base + nargs - 1u - i];
+ g->const_stack[base + i] = g->const_stack[base + nargs - 1u - i];
g->stack[base + nargs - 1u - i] = tmp;
+ g->const_stack[base + nargs - 1u - i] = ctmp;
}
for (u32 i = 0; i < nargs; ++i) api_pack_call_arg(g, &args[i], fty, i);
}
@@ -115,6 +118,10 @@ CGLocal api_alloc_call_result(KitCg* g, KitCgTypeId ret_ty) {
return api_alloc_temp_local(g, ret_ty);
}
+static int api_type_has_value(KitCg* g, KitCgTypeId ty) {
+ return ty != KIT_CG_TYPE_NONE && !cg_type_is_void(g->c, ty);
+}
+
void api_push_call_result(KitCg* g, CGLocal result, KitCgTypeId ret_ty) {
Operand op = api_op_local(result, ret_ty);
/* An aggregate result is a PLACE (it is addressed/copied, never a scalar
@@ -205,6 +212,21 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
compiler_panic(g->c, g->cur_loc, "KitCg: call stack underflow");
return;
}
+ if (api_unevaluated(g)) {
+ u32 i;
+ result_type = cg_type_func_result_id(g->c, fty);
+ for (i = 0; i < nargs; ++i) {
+ ApiSValue arg = api_pop(g);
+ api_release(g, &arg);
+ }
+ callee = api_pop(g);
+ api_release(g, &callee);
+ if (api_type_has_value(g, result_type)) {
+ api_push(g, api_uneval_value(g, result_type));
+ api_const_set_top(g, api_const_unknown(result_type));
+ }
+ return;
+ }
args = api_alloc_call_args(g, nargs);
api_pack_call_args_in_order(g, args, nargs, fty);
@@ -234,7 +256,7 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
desc.flags = emit_tail ? CG_CALL_TAIL : CG_CALL_NONE;
result_type =
emit_tail ? KIT_CG_TYPE_NONE : cg_type_func_result_id(g->c, fty);
- if (result_type != KIT_CG_TYPE_NONE)
+ if (api_type_has_value(g, result_type))
desc.result = api_alloc_call_result(g, result_type);
(void)T;
@@ -261,6 +283,19 @@ void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs,
compiler_panic(g->c, g->cur_loc, "KitCg: call stack underflow");
return;
}
+ if (api_unevaluated(g)) {
+ u32 i;
+ result_type = cg_type_func_result_id(g->c, fty);
+ for (i = 0; i < nargs; ++i) {
+ ApiSValue arg = api_pop(g);
+ api_release(g, &arg);
+ }
+ if (api_type_has_value(g, result_type)) {
+ api_push(g, api_uneval_value(g, result_type));
+ api_const_set_top(g, api_const_unknown(result_type));
+ }
+ return;
+ }
args = api_alloc_call_args(g, nargs);
api_pack_call_args_in_order(g, args, nargs, fty);
callee_op = api_op_global((ObjSymId)sym, 0, cg_type_ptr_to(g->c, fty));
@@ -282,7 +317,7 @@ void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs,
desc.flags = emit_tail ? CG_CALL_TAIL : CG_CALL_NONE;
result_type =
emit_tail ? KIT_CG_TYPE_NONE : cg_type_func_result_id(g->c, fty);
- if (result_type != KIT_CG_TYPE_NONE)
+ if (api_type_has_value(g, result_type))
desc.result = api_alloc_call_result(g, result_type);
api_finish_call(g, &desc, want_tail, emit_tail);
}
@@ -298,7 +333,14 @@ void kit_cg_ret(KitCg* g) {
CGLocal value;
if (!g) return;
rty = g->fn_desc.result_type;
- if (rty == KIT_CG_TYPE_NONE) {
+ if (api_unevaluated(g)) {
+ if (api_type_has_value(g, rty)) {
+ v = api_pop(g);
+ api_release(g, &v);
+ }
+ return;
+ }
+ if (!api_type_has_value(g, rty)) {
g->target->ret(g->target, CG_LOCAL_NONE);
return;
}
diff --git a/src/cg/const.c b/src/cg/const.c
@@ -0,0 +1,552 @@
+#include "cg/internal.h"
+
+static u64 const_mask(u32 width) {
+ if (width >= 64u) return UINT64_MAX;
+ return (1ull << width) - 1ull;
+}
+
+static int const_type_width(KitCg* g, KitCgTypeId type, u32* width_out) {
+ u32 width;
+ if (!g || !width_out) return 0;
+ width = api_int_like_width(g->c, type);
+ if (!width || width > 128u) return 0;
+ *width_out = width;
+ return 1;
+}
+
+static KitCgConstInt const_normalize(KitCgConstInt v, u32 width) {
+ v.width = (uint16_t)width;
+ if (!v.known || width == 0 || width > 128u) {
+ memset(&v, 0, sizeof v);
+ v.width = (uint16_t)width;
+ return v;
+ }
+ if (width < 64u) {
+ v.lo &= const_mask(width);
+ v.hi = 0;
+ } else if (width == 64u) {
+ v.hi = 0;
+ } else if (width < 128u) {
+ v.hi &= const_mask(width - 64u);
+ }
+ v.known = 1;
+ return v;
+}
+
+static int const_is_zero(KitCgConstInt v) {
+ return v.known && v.lo == 0 && v.hi == 0;
+}
+
+static int const_sign_bit(KitCgConstInt v) {
+ u32 width = v.width;
+ if (!width) return 0;
+ if (width <= 64u) return ((v.lo >> (width - 1u)) & 1u) != 0;
+ return ((v.hi >> (width - 65u)) & 1u) != 0;
+}
+
+static int const_cmp_unsigned(KitCgConstInt a, KitCgConstInt b) {
+ if (a.hi != b.hi) return a.hi < b.hi ? -1 : 1;
+ if (a.lo != b.lo) return a.lo < b.lo ? -1 : 1;
+ return 0;
+}
+
+static int const_cmp_signed(KitCgConstInt a, KitCgConstInt b) {
+ int as = const_sign_bit(a);
+ int bs = const_sign_bit(b);
+ if (as != bs) return as ? -1 : 1;
+ return const_cmp_unsigned(a, b);
+}
+
+static u64 const_low_masked(KitCgConstInt v) {
+ if (v.width >= 64u) return v.lo;
+ return v.lo & const_mask(v.width);
+}
+
+static u64 const_abs_signed64_bits(u64 bits, u32 width, int* neg_out) {
+ u64 mask = const_mask(width);
+ int neg = width && ((bits >> (width - 1u)) & 1u) != 0;
+ bits &= mask;
+ if (neg) bits = ((~bits) + 1u) & mask;
+ if (neg_out) *neg_out = neg;
+ return bits;
+}
+
+static int const_divrem64(BinOp op, u32 width, u64 a, u64 b, u64* out) {
+ u64 mask = const_mask(width);
+ if (!out || b == 0) return 0;
+ a &= mask;
+ b &= mask;
+ switch (op) {
+ case BO_UDIV:
+ *out = (a / b) & mask;
+ return 1;
+ case BO_UREM:
+ *out = (a % b) & mask;
+ return 1;
+ case BO_SDIV:
+ case BO_SREM: {
+ int an, bn;
+ u64 aa = const_abs_signed64_bits(a, width, &an);
+ u64 bb = const_abs_signed64_bits(b, width, &bn);
+ u64 r;
+ if (bb == 0) return 0;
+ if (op == BO_SDIV) {
+ r = aa / bb;
+ if (an != bn) r = ((~r) + 1u) & mask;
+ } else {
+ r = aa % bb;
+ if (an) r = ((~r) + 1u) & mask;
+ }
+ *out = r & mask;
+ return 1;
+ }
+ default:
+ return 0;
+ }
+}
+
+static KitCgConstInt const_add(KitCgConstInt a, KitCgConstInt b) {
+ KitCgConstInt r = a;
+ u64 lo = a.lo + b.lo;
+ u64 carry = lo < a.lo;
+ r.lo = lo;
+ r.hi = a.hi + b.hi + carry;
+ return const_normalize(r, r.width);
+}
+
+static KitCgConstInt const_sub(KitCgConstInt a, KitCgConstInt b) {
+ KitCgConstInt r = a;
+ u64 borrow = a.lo < b.lo;
+ r.lo = a.lo - b.lo;
+ r.hi = a.hi - b.hi - borrow;
+ return const_normalize(r, r.width);
+}
+
+static KitCgConstInt const_mul(KitCgConstInt a, KitCgConstInt b) {
+ KitCgConstInt r = a;
+ u16 al[8];
+ u16 bl[8];
+ u16 rl[8];
+ u32 i;
+ u32 k;
+ for (i = 0; i < 4u; ++i) {
+ al[i] = (u16)(a.lo >> (i * 16u));
+ bl[i] = (u16)(b.lo >> (i * 16u));
+ al[i + 4u] = (u16)(a.hi >> (i * 16u));
+ bl[i + 4u] = (u16)(b.hi >> (i * 16u));
+ rl[i] = 0;
+ rl[i + 4u] = 0;
+ }
+ {
+ u64 carry = 0;
+ for (k = 0; k < 8u; ++k) {
+ u64 sum = carry;
+ for (i = 0; i <= k; ++i) sum += (u32)al[i] * (u32)bl[k - i];
+ rl[k] = (u16)sum;
+ carry = sum >> 16;
+ }
+ }
+ r.lo = 0;
+ r.hi = 0;
+ for (i = 0; i < 4u; ++i) {
+ r.lo |= (u64)rl[i] << (i * 16u);
+ r.hi |= (u64)rl[i + 4u] << (i * 16u);
+ }
+ return const_normalize(r, r.width);
+}
+
+static KitCgConstInt const_shl(KitCgConstInt a, u32 sh) {
+ KitCgConstInt r = a;
+ sh &= (u32)(a.width - 1u);
+ if (sh == 0) return const_normalize(r, r.width);
+ if (sh >= 64u) {
+ r.hi = a.lo << (sh - 64u);
+ r.lo = 0;
+ } else {
+ r.hi = (a.hi << sh) | (a.lo >> (64u - sh));
+ r.lo = a.lo << sh;
+ }
+ return const_normalize(r, r.width);
+}
+
+static KitCgConstInt const_lshr(KitCgConstInt a, u32 sh) {
+ KitCgConstInt r = a;
+ sh &= (u32)(a.width - 1u);
+ if (sh == 0) return const_normalize(r, r.width);
+ if (sh >= 64u) {
+ r.lo = a.hi >> (sh - 64u);
+ r.hi = 0;
+ } else {
+ r.lo = (a.lo >> sh) | (a.hi << (64u - sh));
+ r.hi = a.hi >> sh;
+ }
+ return const_normalize(r, r.width);
+}
+
+static KitCgConstInt const_ashr(KitCgConstInt a, u32 sh) {
+ KitCgConstInt r = const_lshr(a, sh);
+ u32 width = a.width;
+ if (!const_sign_bit(a)) return r;
+ sh &= (u32)(width - 1u);
+ if (sh == 0) return r;
+ if (width <= 64u) {
+ u64 fill = ~const_mask(width - sh);
+ r.lo |= fill & const_mask(width);
+ r.hi = 0;
+ } else {
+ u32 top_bits = sh;
+ while (top_bits) {
+ u32 bit = width - top_bits;
+ if (bit < 64u)
+ r.lo |= 1ull << bit;
+ else
+ r.hi |= 1ull << (bit - 64u);
+ --top_bits;
+ }
+ }
+ return const_normalize(r, width);
+}
+
+static KitCgConstInt const_sext(KitCgConstInt v, u32 src_width, u32 dst_width) {
+ int neg;
+ v = const_normalize(v, src_width);
+ neg = const_sign_bit(v);
+ v.width = (uint16_t)dst_width;
+ if (neg && dst_width > src_width) {
+ u32 bit;
+ for (bit = src_width; bit < dst_width; ++bit) {
+ if (bit < 64u)
+ v.lo |= 1ull << bit;
+ else
+ v.hi |= 1ull << (bit - 64u);
+ }
+ }
+ return const_normalize(v, dst_width);
+}
+
+int api_unevaluated(KitCg* g) { return g && g->unevaluated_depth != 0; }
+
+ApiSValue api_uneval_value(KitCg* g, KitCgTypeId type) {
+ KitCgTypeId ty = type ? resolve_type(g->c, type) : KIT_CG_TYPE_NONE;
+ return api_make_sv(api_op_imm(0, ty), ty);
+}
+
+ApiSValue api_uneval_place(KitCg* g, KitCgTypeId type) {
+ KitCgTypeId ty = type ? resolve_type(g->c, type) : KIT_CG_TYPE_NONE;
+ return api_make_lv(api_op_indirect(CG_LOCAL_NONE, 0, ty), ty);
+}
+
+ApiConstValue api_const_unknown(KitCgTypeId type) {
+ ApiConstValue cv;
+ memset(&cv, 0, sizeof cv);
+ cv.type = type;
+ return cv;
+}
+
+ApiConstValue api_const_int_result(KitCg* g, KitCgTypeId type, u64 lo, u64 hi,
+ int is_signed) {
+ ApiConstValue cv = api_const_unknown(type);
+ u32 width;
+ if (!const_type_width(g, type, &width)) return cv;
+ cv.value.lo = lo;
+ cv.value.hi = hi;
+ cv.value.width = (uint16_t)width;
+ cv.value.is_signed = is_signed ? 1u : 0u;
+ cv.value.known = 1u;
+ cv.value = const_normalize(cv.value, width);
+ return cv;
+}
+
+ApiConstValue api_const_for_push(KitCg* g, KitCgTypeId type,
+ const KitCgConstInt* value) {
+ ApiConstValue cv = api_const_unknown(type);
+ u32 width;
+ if (!value || !value->known || !const_type_width(g, type, &width)) return cv;
+ cv.type = type;
+ cv.value = const_normalize(*value, width);
+ cv.value.known = 1u;
+ return cv;
+}
+
+ApiConstValue api_const_from_sv(KitCg* g, const ApiSValue* sv) {
+ KitCgTypeId type;
+ if (!g || !sv) return api_const_unknown(KIT_CG_TYPE_NONE);
+ type = api_sv_type(sv);
+ if (api_sv_kind(sv) == SV_OPERAND && sv->op.kind == OPK_IMM) {
+ return api_const_int_result(g, type, (u64)sv->op.v.imm, 0, 0);
+ }
+ return api_const_unknown(type);
+}
+
+ApiConstValue api_const_at(KitCg* g, u32 depth) {
+ if (!g || depth >= g->sp || !g->const_stack)
+ return api_const_unknown(KIT_CG_TYPE_NONE);
+ return g->const_stack[g->sp - 1u - depth];
+}
+
+void api_const_set_top(KitCg* g, ApiConstValue value) {
+ if (!g || !g->sp || !g->const_stack) return;
+ g->const_stack[g->sp - 1u] = value;
+}
+
+void api_const_set_at(KitCg* g, u32 depth, ApiConstValue value) {
+ if (!g || depth >= g->sp || !g->const_stack) return;
+ g->const_stack[g->sp - 1u - depth] = value;
+}
+
+void api_const_copy_top_from(KitCg* g, ApiConstValue value) {
+ api_const_set_top(g, value);
+}
+
+int api_const_fold_binop(KitCg* g, BinOp op, KitCgTypeId type, ApiConstValue a,
+ ApiConstValue b, u32 flags, ApiConstValue* out) {
+ ApiConstValue r;
+ u32 width;
+ if (!out) return 0;
+ r = api_const_unknown(type);
+ if (!g || flags || !a.value.known || !b.value.known ||
+ !const_type_width(g, type, &width)) {
+ *out = r;
+ return 0;
+ }
+ a.value = const_normalize(a.value, width);
+ b.value = const_normalize(b.value, width);
+ r = api_const_int_result(g, type, 0, 0, a.value.is_signed);
+ switch (op) {
+ case BO_IADD:
+ r.value = const_add(a.value, b.value);
+ break;
+ case BO_ISUB:
+ r.value = const_sub(a.value, b.value);
+ break;
+ case BO_IMUL:
+ r.value = const_mul(a.value, b.value);
+ break;
+ case BO_AND:
+ r.value.lo = a.value.lo & b.value.lo;
+ r.value.hi = a.value.hi & b.value.hi;
+ r.value = const_normalize(r.value, width);
+ break;
+ case BO_OR:
+ r.value.lo = a.value.lo | b.value.lo;
+ r.value.hi = a.value.hi | b.value.hi;
+ r.value = const_normalize(r.value, width);
+ break;
+ case BO_XOR:
+ r.value.lo = a.value.lo ^ b.value.lo;
+ r.value.hi = a.value.hi ^ b.value.hi;
+ r.value = const_normalize(r.value, width);
+ break;
+ case BO_SHL:
+ r.value = const_shl(a.value, (u32)b.value.lo);
+ break;
+ case BO_SHR_U:
+ r.value = const_lshr(a.value, (u32)b.value.lo);
+ break;
+ case BO_SHR_S:
+ r.value = const_ashr(a.value, (u32)b.value.lo);
+ break;
+ case BO_SDIV:
+ case BO_UDIV:
+ case BO_SREM:
+ case BO_UREM: {
+ u64 folded;
+ if (width > 64u || !const_divrem64(op, width, const_low_masked(a.value),
+ const_low_masked(b.value), &folded)) {
+ *out = api_const_unknown(type);
+ return 0;
+ }
+ r.value =
+ api_const_int_result(g, type, folded, 0, a.value.is_signed).value;
+ break;
+ }
+ default:
+ *out = api_const_unknown(type);
+ return 0;
+ }
+ r.type = type;
+ r.value.known = 1u;
+ *out = r;
+ return 1;
+}
+
+int api_const_fold_unop(KitCg* g, UnOp op, KitCgTypeId type, ApiConstValue a,
+ u32 flags, ApiConstValue* out) {
+ ApiConstValue r;
+ u32 width;
+ if (!out) return 0;
+ r = api_const_unknown(type);
+ if (!g || flags || !a.value.known || !const_type_width(g, type, &width)) {
+ *out = r;
+ return 0;
+ }
+ a.value = const_normalize(a.value, width);
+ r = api_const_int_result(g, type, 0, 0, a.value.is_signed);
+ switch (op) {
+ case UO_NEG:
+ r.value.lo = ~a.value.lo;
+ r.value.hi = ~a.value.hi;
+ r.value = const_add(const_normalize(r.value, width),
+ api_const_int_result(g, type, 1, 0, 0).value);
+ break;
+ case UO_NOT:
+ r.value.lo = const_is_zero(a.value) ? 1u : 0u;
+ r.value.hi = 0;
+ r.value = const_normalize(r.value, width);
+ break;
+ case UO_BNOT:
+ r.value.lo = ~a.value.lo;
+ r.value.hi = ~a.value.hi;
+ r.value = const_normalize(r.value, width);
+ break;
+ default:
+ *out = api_const_unknown(type);
+ return 0;
+ }
+ r.type = type;
+ r.value.known = 1u;
+ *out = r;
+ return 1;
+}
+
+int api_const_fold_cmp(KitCg* g, CmpOp op, ApiConstValue a, ApiConstValue b,
+ ApiConstValue* out) {
+ KitCgTypeId bool_ty = builtin_id(KIT_CG_BUILTIN_I32);
+ ApiConstValue r;
+ u32 width = a.value.width ? a.value.width : b.value.width;
+ int cmp;
+ int ok;
+ if (!out) return 0;
+ r = api_const_unknown(bool_ty);
+ if (!g || !a.value.known || !b.value.known || !width || width > 128u) {
+ *out = r;
+ return 0;
+ }
+ a.value = const_normalize(a.value, width);
+ b.value = const_normalize(b.value, width);
+ switch (op) {
+ case CMP_EQ:
+ ok = a.value.lo == b.value.lo && a.value.hi == b.value.hi;
+ break;
+ case CMP_NE:
+ ok = a.value.lo != b.value.lo || a.value.hi != b.value.hi;
+ break;
+ case CMP_LT_U:
+ ok = const_cmp_unsigned(a.value, b.value) < 0;
+ break;
+ case CMP_LE_U:
+ ok = const_cmp_unsigned(a.value, b.value) <= 0;
+ break;
+ case CMP_GT_U:
+ ok = const_cmp_unsigned(a.value, b.value) > 0;
+ break;
+ case CMP_GE_U:
+ ok = const_cmp_unsigned(a.value, b.value) >= 0;
+ break;
+ case CMP_LT_S:
+ cmp = const_cmp_signed(a.value, b.value);
+ ok = cmp < 0;
+ break;
+ case CMP_LE_S:
+ cmp = const_cmp_signed(a.value, b.value);
+ ok = cmp <= 0;
+ break;
+ case CMP_GT_S:
+ cmp = const_cmp_signed(a.value, b.value);
+ ok = cmp > 0;
+ break;
+ case CMP_GE_S:
+ cmp = const_cmp_signed(a.value, b.value);
+ ok = cmp >= 0;
+ break;
+ default:
+ *out = r;
+ return 0;
+ }
+ r = api_const_int_result(g, bool_ty, ok ? 1u : 0u, 0, 0);
+ *out = r;
+ return 1;
+}
+
+int api_const_fold_convert(KitCg* g, ConvKind ck, KitCgTypeId src_type,
+ KitCgTypeId dst_type, ApiConstValue in,
+ ApiConstValue* out) {
+ ApiConstValue r;
+ u32 sw;
+ u32 dw;
+ if (!out) return 0;
+ r = api_const_unknown(dst_type);
+ if (!g || !in.value.known || !const_type_width(g, src_type, &sw) ||
+ !const_type_width(g, dst_type, &dw)) {
+ *out = r;
+ return 0;
+ }
+ switch (ck) {
+ case CV_TRUNC:
+ case CV_ZEXT:
+ r = api_const_int_result(g, dst_type, in.value.lo, in.value.hi,
+ in.value.is_signed);
+ break;
+ case CV_SEXT:
+ r = api_const_int_result(g, dst_type, 0, 0, 1);
+ r.value = const_sext(in.value, sw, dw);
+ r.value.known = 1u;
+ r.value.is_signed = 1u;
+ break;
+ case CV_BITCAST:
+ if (sw != dw) {
+ *out = r;
+ return 0;
+ }
+ r = api_const_int_result(g, dst_type, in.value.lo, in.value.hi,
+ in.value.is_signed);
+ break;
+ default:
+ *out = r;
+ return 0;
+ }
+ r.type = dst_type;
+ *out = r;
+ return r.value.known != 0;
+}
+
+void kit_cg_unevaluated_push(KitCg* g) {
+ if (!g) return;
+ ++g->unevaluated_depth;
+ if (g->unevaluated_depth == 0) {
+ compiler_panic(g->c, g->cur_loc, "KitCg: unevaluated depth overflow");
+ g->unevaluated_depth = UINT32_MAX;
+ }
+}
+
+void kit_cg_unevaluated_pop(KitCg* g) {
+ if (!g) return;
+ if (!g->unevaluated_depth) {
+ compiler_panic(g->c, g->cur_loc, "KitCg: unevaluated pop underflow");
+ return;
+ }
+ --g->unevaluated_depth;
+}
+
+int kit_cg_top_const_int_ex(KitCg* g, KitCgConstInt* out_value) {
+ ApiConstValue cv;
+ if (!g || !g->sp) return 0;
+ cv = api_const_at(g, 0);
+ if (!cv.value.known) return 0;
+ if (out_value) *out_value = cv.value;
+ return 1;
+}
+
+int kit_cg_top_const_i64(KitCg* g, int64_t* out_value) {
+ KitCgConstInt v;
+ if (!out_value || !kit_cg_top_const_int_ex(g, &v) || v.width > 64u) return 0;
+ if (v.is_signed)
+ *out_value = api_sign_extend_width(v.lo, v.width);
+ else
+ *out_value = (int64_t)const_low_masked(v);
+ return 1;
+}
+
+int kit_cg_top_const_int(KitCg* g, int64_t* out_value) {
+ return kit_cg_top_const_i64(g, out_value);
+}
diff --git a/src/cg/control.c b/src/cg/control.c
@@ -3,17 +3,20 @@
KitCgLabel kit_cg_label_new(KitCg* g) {
if (!g) return KIT_CG_LABEL_NONE;
+ if (api_unevaluated(g)) return KIT_CG_LABEL_NONE;
return (KitCgLabel)g->target->label_new(g->target);
}
void kit_cg_label_place(KitCg* g, KitCgLabel label) {
if (!g) return;
+ if (api_unevaluated(g)) return;
api_local_const_control_boundary(g);
g->target->label_place(g->target, (Label)label);
}
void kit_cg_jump(KitCg* g, KitCgLabel label) {
if (!g) return;
+ if (api_unevaluated(g)) return;
api_local_const_control_boundary(g);
g->target->jump(g->target, (Label)label);
}
@@ -22,15 +25,19 @@ void api_branch_if(KitCg* g, ApiSValue* v, int branch_when_true, Label label) {
CgTarget* T;
KitCgTypeId ty;
if (!g) return;
+ if (api_unevaluated(g)) {
+ api_release(g, v);
+ return;
+ }
api_local_const_control_boundary(g);
T = g->target;
ty = v->type ? v->type : builtin_id(KIT_CG_BUILTIN_I32);
- if (v->op.kind == OPK_IMM && v->kind == SV_OPERAND) {
+ if (v->op.kind == OPK_IMM && api_sv_kind(v) == SV_OPERAND) {
if ((v->op.v.imm != 0) == !!branch_when_true) T->jump(T, label);
api_release(g, v);
return;
}
- if (v->kind == SV_CMP) {
+ if (api_sv_kind(v) == SV_CMP) {
CmpOp op = branch_when_true ? v->delayed->cmp.op
: api_invert_cmp(v->delayed->cmp.op);
/* Flag dead-transient operands so the -O0 backend drops them at the branch
@@ -130,36 +137,72 @@ typedef struct CGSwitchPlan {
u64 span;
} CGSwitchPlan;
-/* Single pass over the cases array: derive (vmin, vmax, span) in the
- * selector type's signed interpretation. Frontends store case values as
- * the u64 bit pattern of an i64 sign-extended from the selector's
- * width, so api_sign_extend_width recovers the source ordering for
- * both signed and unsigned selectors that fit in i64. Returns 0 if the
- * selector type is unusable for our policy (>64 bits) or if span
- * overflows u64. */
+/* Single pass over the cases array, deriving (vmin, span) for the tightest
+ * dispatch window. Dispatch is `idx = (sel - vmin) mod 2^width` with an
+ * unsigned bounds check, so the cases live on a circle of size 2^width and
+ * the minimal enclosing window is the complement of the largest gap between
+ * them. We don't sort to find that gap; instead we evaluate the two windows
+ * whose seam sits at a fixed point and take the smaller:
+ *
+ * - the *signed* window [smin, smax], seam at the signed midpoint — tight
+ * for values near 0 or near 2^width (e.g. a (T)-1 case folded next to
+ * small positives);
+ * - the *unsigned* window [umin, umax], seam at 0 / 2^width — tight for
+ * values straddling the signed midpoint.
+ *
+ * A table-eligible cluster (span <= a few thousand) is far smaller than
+ * 2^(width-1), so it can cross at most one of those two seams; the smaller
+ * window is therefore the true minimum. Selector signedness isn't even
+ * visible here (CG integer builtins carry only a width), and both windows
+ * dispatch correctly under the modular index, so taking the min is a pure
+ * code-size win. Returns 0 if the selector type is unusable (>64 bits) or
+ * both windows degenerate to the full range. */
static int cg_switch_extents(Compiler* c, const CGSwitchDesc* d, i64* out_vmin,
u64* out_span) {
u32 width;
- i64 vmin;
- i64 vmax;
+ u64 mask;
+ i64 smin;
+ i64 smax;
+ u64 umin;
+ u64 umax;
u32 i;
+ u64 sspan;
+ u64 uspan;
width = kit_cg_type_int_width((KitCompiler*)c, d->selector_type);
if (!width || width > 64u) return 0;
- vmin = INT64_MAX;
- vmax = INT64_MIN;
+ if (d->ncases == 0) return 0;
+ mask = (width >= 64u) ? UINT64_MAX : (((u64)1u << width) - 1u);
+ smin = INT64_MAX;
+ smax = INT64_MIN;
+ umin = UINT64_MAX;
+ umax = 0;
for (i = 0; i < d->ncases; ++i) {
- i64 vi = (width == 64u) ? (i64)d->cases[i].value
+ u64 raw = d->cases[i].value & mask;
+ i64 sv = (width == 64u) ? (i64)d->cases[i].value
: api_sign_extend_width(d->cases[i].value, width);
- if (vi < vmin) vmin = vi;
- if (vi > vmax) vmax = vi;
+ if (sv < smin) smin = sv;
+ if (sv > smax) smax = sv;
+ if (raw < umin) umin = raw;
+ if (raw > umax) umax = raw;
}
- if (vmax < vmin) return 0;
+ /* span = delta + 1; a delta of UINT64_MAX is the degenerate full-range
+ * case the table can't represent. */
{
- u64 delta = (u64)vmax - (u64)vmin;
- if (delta == UINT64_MAX) return 0;
- *out_span = delta + 1u;
+ u64 sdelta = (u64)smax - (u64)smin;
+ sspan = sdelta == UINT64_MAX ? 0u : sdelta + 1u;
+ }
+ {
+ u64 udelta = umax - umin;
+ uspan = udelta == UINT64_MAX ? 0u : udelta + 1u;
+ }
+ if (sspan == 0u && uspan == 0u) return 0;
+ if (uspan != 0u && (sspan == 0u || uspan < sspan)) {
+ *out_span = uspan;
+ *out_vmin = (i64)umin; /* a bit pattern; emission indexes modularly */
+ } else {
+ *out_span = sspan;
+ *out_vmin = smin;
}
- *out_vmin = vmin;
return 1;
}
@@ -255,11 +298,16 @@ static void cg_emit_switch_table(KitCg* g, const CGSwitchDesc* d,
if (!labels) compiler_panic(c, g->cur_loc, "kit_cg_switch: oom");
for (i = 0; i < plan->span; ++i) labels[i] = d->default_label;
width = sel_w;
- for (i = 0; i < d->ncases; ++i) {
- i64 vi = (width == 64u) ? (i64)d->cases[i].value
- : api_sign_extend_width(d->cases[i].value, width);
- u64 table_index = (u64)(vi - plan->vmin);
- labels[table_index] = d->cases[i].label;
+ {
+ /* Index modularly: vmin may be the unsigned-window origin (a value with
+ * the sign bit set), so a signed `vi - vmin` could overflow. This mirrors
+ * the runtime `idx = sel - vmin` computed in sel_ty, and reproduces the
+ * old non-wrapping subtraction exactly for the signed-window case. */
+ u64 mask = (width >= 64u) ? UINT64_MAX : (((u64)1u << width) - 1u);
+ for (i = 0; i < d->ncases; ++i) {
+ u64 table_index = ((u64)d->cases[i].value - (u64)plan->vmin) & mask;
+ labels[table_index] = d->cases[i].label;
+ }
}
table_sym = api_emit_label_table(g, labels, (u32)plan->span);
h->free(h, labels, (size_t)plan->span * sizeof *labels);
@@ -313,6 +361,11 @@ void kit_cg_switch(KitCg* g, KitCgSwitch sw) {
int native_switch_override;
if (!g) return;
if (g->sp == 0) return;
+ if (api_unevaluated(g)) {
+ selector = api_pop(g);
+ api_release(g, &selector);
+ return;
+ }
api_local_const_control_boundary(g);
memset(&desc, 0, sizeof desc);
desc.selector_type = resolve_type(g->c, sw.selector_type);
@@ -385,6 +438,11 @@ void kit_cg_push_label_addr(KitCg* g, KitCgLabel label, KitCgTypeId ptr_type) {
if (!g) return;
ty = resolve_type(g->c, ptr_type);
if (!ty) ty = cg_type_ptr_to(g->c, builtin_id(KIT_CG_BUILTIN_VOID));
+ if (api_unevaluated(g)) {
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, api_const_unknown(ty));
+ return;
+ }
r = api_alloc_temp_local(g, ty);
dst = api_op_local(r, ty);
g->target->load_label_addr(g->target, dst, (Label)label);
@@ -397,6 +455,11 @@ void kit_cg_computed_goto(KitCg* g, const KitCgLabel* valid_targets,
KitCgTypeId target_ty;
Operand target_op;
if (!g) return;
+ if (api_unevaluated(g)) {
+ target = api_pop(g);
+ api_release(g, &target);
+ return;
+ }
if (!valid_targets || ntargets == 0) {
compiler_panic(g->c, g->cur_loc,
"kit_cg_computed_goto: valid_targets must be non-empty");
@@ -413,6 +476,7 @@ void kit_cg_computed_goto(KitCg* g, const KitCgLabel* valid_targets,
void kit_cg_unreachable(KitCg* g) {
if (!g) return;
+ if (api_unevaluated(g)) return;
api_local_const_control_boundary(g);
g->target->unreachable(g->target);
}
@@ -498,12 +562,17 @@ static void api_scope_store_one(KitCg* g, CGLocal local, KitCgTypeId type,
}
/* Reload one carry local onto the value stack (fresh temp, load, push). */
-static void api_scope_reload_one(KitCg* g, CGLocal local, KitCgTypeId type) {
+static void api_scope_reload_one(KitCg* g, CGLocal local, KitCgTypeId type,
+ const void* lang_type, u16 lang_flags) {
CGLocal r = api_alloc_temp_local(g, type);
Operand dst = api_op_local(r, type);
Operand src = api_op_local(local, type);
+ ApiSValue sv;
g->target->load(g->target, dst, src, api_mem_for_lvalue(g, &src, type));
- api_push(g, api_make_sv(dst, type));
+ sv = api_make_sv(dst, type);
+ sv.lang_type = lang_type;
+ sv.lang_flags = lang_flags;
+ api_push(g, sv);
}
/* Pop the scope's N results into a caller array (out[i] = result i, value-stack
@@ -519,6 +588,8 @@ void api_scope_store_results(KitCg* g, ApiCgScope* s) {
for (k = 0; k < s->nresults; ++k) {
u32 idx = s->nresults - 1u - k; /* TOS popped first -> highest index */
ApiSValue v = api_pop(g);
+ s->result_lang_types[idx] = v.lang_type;
+ s->result_lang_flags[idx] = v.lang_flags;
api_scope_store_one(g, s->result_locals[idx], s->result_types[idx], &v);
}
}
@@ -527,7 +598,8 @@ void api_scope_store_results(KitCg* g, ApiCgScope* s) {
void api_scope_push_results(KitCg* g, ApiCgScope* s) {
u32 k;
for (k = 0; k < s->nresults; ++k)
- api_scope_reload_one(g, s->result_locals[k], s->result_types[k]);
+ api_scope_reload_one(g, s->result_locals[k], s->result_types[k],
+ s->result_lang_types[k], s->result_lang_flags[k]);
}
/* Pop N params off the value stack into the loop-param carry locals. */
@@ -536,6 +608,8 @@ static void api_scope_store_params(KitCg* g, ApiCgScope* s) {
for (k = 0; k < s->nparams; ++k) {
u32 idx = s->nparams - 1u - k;
ApiSValue v = api_pop(g);
+ s->param_lang_types[idx] = v.lang_type;
+ s->param_lang_flags[idx] = v.lang_flags;
api_scope_store_one(g, s->param_locals[idx], s->param_types[idx], &v);
}
}
@@ -544,7 +618,8 @@ static void api_scope_store_params(KitCg* g, ApiCgScope* s) {
static void api_scope_reload_params(KitCg* g, ApiCgScope* s) {
u32 k;
for (k = 0; k < s->nparams; ++k)
- api_scope_reload_one(g, s->param_locals[k], s->param_types[k]);
+ api_scope_reload_one(g, s->param_locals[k], s->param_types[k],
+ s->param_lang_types[k], s->param_lang_flags[k]);
}
/* Allocate a memory-resident carry local of the given type. `resolved` is the
@@ -575,8 +650,12 @@ static int api_scope_setup_sig(KitCg* g, ApiCgScope* s,
if (nr <= API_CG_SCOPE_SIG_INLINE && np <= API_CG_SCOPE_SIG_INLINE) {
s->result_locals = s->result_locals_inl;
s->result_types = s->result_types_inl;
+ s->result_lang_types = s->result_lang_types_inl;
+ s->result_lang_flags = s->result_lang_flags_inl;
s->param_locals = s->param_locals_inl;
s->param_types = s->param_types_inl;
+ s->param_lang_types = s->param_lang_types_inl;
+ s->param_lang_flags = s->param_lang_flags_inl;
return 1;
}
{
@@ -591,17 +670,46 @@ static int api_scope_setup_sig(KitCg* g, ApiCgScope* s,
s->result_types = blk + nr;
s->param_locals = blk + 2u * nr;
s->param_types = blk + 2u * nr + np;
+ s->heap_lang_types_block =
+ (const void**)h->alloc(h, sizeof(void*) * (nr + np), _Alignof(void*));
+ s->heap_lang_flags_block =
+ (u16*)h->alloc(h, sizeof(u16) * (nr + np), _Alignof(u16));
+ if (!s->heap_lang_types_block || !s->heap_lang_flags_block) {
+ if (s->heap_lang_types_block)
+ h->free(h, s->heap_lang_types_block, sizeof(void*) * (nr + np));
+ if (s->heap_lang_flags_block)
+ h->free(h, s->heap_lang_flags_block, sizeof(u16) * (nr + np));
+ h->free(h, blk, sizeof(u32) * total);
+ s->heap_block = NULL;
+ s->heap_lang_types_block = NULL;
+ s->heap_lang_flags_block = NULL;
+ return 0;
+ }
+ s->result_lang_types = s->heap_lang_types_block;
+ s->param_lang_types = s->heap_lang_types_block + nr;
+ s->result_lang_flags = s->heap_lang_flags_block;
+ s->param_lang_flags = s->heap_lang_flags_block + nr;
return 1;
}
}
static void api_scope_free_sig(KitCg* g, ApiCgScope* s) {
+ Heap* h = g->c->ctx->heap;
if (s->heap_block) {
- Heap* h = g->c->ctx->heap;
u32 total = 2u * s->nresults + 2u * s->nparams;
h->free(h, s->heap_block, sizeof(u32) * total);
s->heap_block = NULL;
}
+ if (s->heap_lang_types_block) {
+ h->free(h, s->heap_lang_types_block,
+ sizeof(void*) * (s->nresults + s->nparams));
+ s->heap_lang_types_block = NULL;
+ }
+ if (s->heap_lang_flags_block) {
+ h->free(h, s->heap_lang_flags_block,
+ sizeof(u16) * (s->nresults + s->nparams));
+ s->heap_lang_flags_block = NULL;
+ }
}
static KitCgScope api_scope_begin_sig_kind(KitCg* g, u8 kind,
@@ -683,23 +791,47 @@ static KitCgScope api_scope_begin_sig_kind(KitCg* g, u8 kind,
return api_scope_handle(idx, s->generation);
}
-KitCgScope kit_cg_scope_begin(KitCg* g, KitCgTypeId result_type) {
+KitCgScope kit_cg_scope_begin(KitCg* g) {
+ KitCgScopeSig sig;
+ memset(&sig, 0, sizeof sig);
+ return api_scope_begin_sig_kind(g, (u8)SCOPE_LOOP, &sig);
+}
+
+KitCgScope kit_cg_scope_begin_value(KitCg* g, KitCgTypeId result_type) {
KitCgScopeSig sig;
+ KitCgTypeId resolved;
memset(&sig, 0, sizeof sig);
- if (g && resolve_type(g->c, result_type) != KIT_CG_TYPE_NONE) {
- sig.results = &result_type;
- sig.nresults = 1u;
+ if (!g) return 0;
+ resolved = resolve_type(g->c, result_type);
+ if (resolved == KIT_CG_TYPE_NONE || cg_type_is_void(g->c, resolved)) {
+ compiler_panic(g->c, g->cur_loc,
+ "KitCg: value scope requires non-void result type");
+ return 0;
}
+ sig.results = &result_type;
+ sig.nresults = 1u;
return api_scope_begin_sig_kind(g, (u8)SCOPE_LOOP, &sig);
}
-KitCgScope kit_cg_block_begin(KitCg* g, KitCgTypeId result_type) {
+KitCgScope kit_cg_block_begin(KitCg* g) {
+ KitCgScopeSig sig;
+ memset(&sig, 0, sizeof sig);
+ return api_scope_begin_sig_kind(g, (u8)SCOPE_BLOCK, &sig);
+}
+
+KitCgScope kit_cg_block_begin_value(KitCg* g, KitCgTypeId result_type) {
KitCgScopeSig sig;
+ KitCgTypeId resolved;
memset(&sig, 0, sizeof sig);
- if (g && resolve_type(g->c, result_type) != KIT_CG_TYPE_NONE) {
- sig.results = &result_type;
- sig.nresults = 1u;
+ if (!g) return 0;
+ resolved = resolve_type(g->c, result_type);
+ if (resolved == KIT_CG_TYPE_NONE || cg_type_is_void(g->c, resolved)) {
+ compiler_panic(g->c, g->cur_loc,
+ "KitCg: value block requires non-void result type");
+ return 0;
}
+ sig.results = &result_type;
+ sig.nresults = 1u;
return api_scope_begin_sig_kind(g, (u8)SCOPE_BLOCK, &sig);
}
@@ -777,8 +909,11 @@ void kit_cg_scope_end_unreachable(KitCg* g, KitCgScope scope) {
static void api_scope_store_results_from(KitCg* g, ApiCgScope* s,
ApiSValue* rs) {
u32 k;
- for (k = 0; k < s->nresults; ++k)
+ for (k = 0; k < s->nresults; ++k) {
+ s->result_lang_types[k] = rs[k].lang_type;
+ s->result_lang_flags[k] = rs[k].lang_flags;
api_scope_store_one(g, s->result_locals[k], s->result_types[k], &rs[k]);
+ }
}
/* Shared body of break_true / break_false: pop the condition, then — when the
@@ -805,7 +940,7 @@ static void api_break_cond(KitCg* g, KitCgScope scope, int break_when,
_Alignof(ApiSValue));
if (!rs) compiler_panic(g->c, g->cur_loc, "KitCg: out of memory");
api_scope_pop_results(g, s, rs);
- if (cond.kind == SV_OPERAND && cond.op.kind == OPK_IMM) {
+ if (api_sv_kind(&cond) == SV_OPERAND && cond.op.kind == OPK_IMM) {
if ((cond.op.v.imm != 0) == !!break_when) {
api_scope_store_results_from(g, s, rs);
api_local_const_control_boundary(g);
@@ -895,6 +1030,12 @@ void kit_cg_alloca(KitCg* g, uint32_t align, KitCgTypeId result_ptr_type) {
sz = api_pop(g);
pty = resolve_type(g->c, result_ptr_type);
if (!pty) pty = cg_type_ptr_to(g->c, builtin_id(KIT_CG_BUILTIN_VOID));
+ if (api_unevaluated(g)) {
+ api_release(g, &sz);
+ api_push(g, api_uneval_value(g, pty));
+ api_const_set_top(g, api_const_unknown(pty));
+ return;
+ }
sz_op = api_sv_op_is(&sz, OPK_IMM)
? sz.op
: api_force_local(g, &sz, api_sv_type(&sz));
@@ -912,6 +1053,10 @@ void kit_cg_vararg_start(KitCg* g) {
if (!g) return;
T = g->target;
ap = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &ap);
+ return;
+ }
ap_op = api_force_local(g, &ap, api_sv_type(&ap));
T->va_start_(T, ap_op);
api_release(g, &ap);
@@ -929,6 +1074,12 @@ void kit_cg_vararg_next(KitCg* g, KitCgTypeId type) {
ty = resolve_type(g->c, type);
if (!ty) return;
ap = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &ap);
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, api_const_unknown(ty));
+ return;
+ }
ap_op = api_force_local(g, &ap, api_sv_type(&ap));
rr = api_alloc_temp_local(g, ty);
dst = api_op_local(rr, ty);
@@ -944,6 +1095,10 @@ void kit_cg_vararg_end(KitCg* g) {
if (!g) return;
T = g->target;
ap = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &ap);
+ return;
+ }
ap_op = api_force_local(g, &ap, api_sv_type(&ap));
T->va_end_(T, ap_op);
api_release(g, &ap);
@@ -957,6 +1112,11 @@ void kit_cg_vararg_copy(KitCg* g) {
T = g->target;
src = api_pop(g);
dst = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &src);
+ api_release(g, &dst);
+ return;
+ }
src_op = api_force_local(g, &src, api_sv_type(&src));
dst_op = api_force_local(g, &dst, api_sv_type(&dst));
T->va_copy_(T, dst_op, src_op);
@@ -984,6 +1144,11 @@ void kit_cg_memcpy(KitCg* g, uint64_t size, KitCgMemAccess dst_access,
T = g->target;
src = api_pop(g);
dst = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &src);
+ api_release(g, &dst);
+ return;
+ }
api_require_pointer_value(g, "memcpy destination", api_sv_type(&dst));
api_require_pointer_value(g, "memcpy source", api_sv_type(&src));
dst_op = api_force_local(g, &dst, api_sv_type(&dst));
@@ -1010,6 +1175,11 @@ void kit_cg_memmove(KitCg* g, uint64_t size, KitCgMemAccess dst_access,
}
src = api_pop(g);
dst = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &src);
+ api_release(g, &dst);
+ return;
+ }
api_require_pointer_value(g, "memmove destination", api_sv_type(&dst));
api_require_pointer_value(g, "memmove source", api_sv_type(&src));
args[0] = api_force_local(g, &dst, api_sv_type(&dst));
@@ -1034,6 +1204,10 @@ void kit_cg_memset(KitCg* g, uint8_t val, uint64_t size,
}
T = g->target;
dst = api_pop(g);
+ if (api_unevaluated(g)) {
+ api_release(g, &dst);
+ return;
+ }
api_require_pointer_value(g, "memset destination", api_sv_type(&dst));
dst_op = api_force_local(g, &dst, api_sv_type(&dst));
byte_val = api_op_imm((i64)val, KIT_CG_TYPE_NONE);
@@ -1097,6 +1271,13 @@ static void api_cg_elem(KitCg* g, u32 elem_size, int64_t offset) {
elemsz = elem_size ? elem_size : (u32)abi_cg_sizeof(g->c->abi, elem_ty);
idx_ty = idx.type ? idx.type : idx.op.type;
if (!idx_ty) idx_ty = builtin_id(KIT_CG_BUILTIN_I64);
+ if (api_unevaluated(g)) {
+ api_release(g, &base);
+ api_release(g, &idx);
+ api_push(g, api_uneval_place(g, elem_ty));
+ api_const_set_top(g, api_const_unknown(elem_ty));
+ return;
+ }
idx_op = api_force_local_unless_imm(g, &idx, idx_ty);
/* Constant index folds entirely into the displacement — no instructions, just
@@ -1107,7 +1288,7 @@ static void api_cg_elem(KitCg* g, u32 elem_size, int64_t offset) {
i64 ofs;
Operand place;
if (cg_checked_scaled_offset(idx_op.v.imm, elemsz, offset, &ofs) &&
- base.kind == SV_OPERAND && base.op.kind == OPK_GLOBAL &&
+ api_sv_kind(&base) == SV_OPERAND && base.op.kind == OPK_GLOBAL &&
!__builtin_add_overflow(base.op.v.global.addend, ofs, &ofs)) {
place = api_op_global(base.op.v.global.sym, ofs, elem_ty);
} else {
@@ -1212,9 +1393,84 @@ void kit_cg_elem_scaled(KitCg* g, uint32_t elem_size, int64_t offset) {
api_cg_elem(g, elem_size, offset);
}
+static void api_cg_field_at_place(KitCg* g, ApiSValue base,
+ int64_t field_offset, KitCgTypeId field_ty) {
+ CgTarget* T;
+ KitCgTypeId base_ty;
+ KitCgTypeId base_ptr_ty;
+ Operand result;
+ if (!g) return;
+ T = g->target;
+ api_ensure_local(g, &base);
+ if (!api_is_lvalue_sv(&base)) {
+ compiler_panic(g->c, g->cur_loc,
+ "KitCg: field_at requires a place; deref a pointer first");
+ api_release(g, &base);
+ return;
+ }
+ field_ty = resolve_type(g->c, field_ty);
+ if (!field_ty) {
+ compiler_panic(g->c, g->cur_loc, "KitCg: field_at has invalid field type");
+ api_release(g, &base);
+ return;
+ }
+ if (api_unevaluated(g)) {
+ api_release(g, &base);
+ api_push(g, api_uneval_place(g, field_ty));
+ api_const_set_top(g, api_const_unknown(field_ty));
+ return;
+ }
+ base_ty = api_sv_type(&base);
+ if (!base_ty) base_ty = builtin_id(KIT_CG_BUILTIN_VOID);
+ base_ptr_ty = cg_type_ptr_to(g->c, base_ty);
+ if (base.op.kind == OPK_GLOBAL) {
+ i64 addend;
+ if (!__builtin_add_overflow(base.op.v.global.addend, field_offset,
+ &addend)) {
+ result = api_op_global(base.op.v.global.sym, addend, field_ty);
+ api_release(g, &base);
+ api_push(g, api_make_lv(result, field_ty));
+ return;
+ }
+ } else if (base.op.kind == OPK_INDIRECT && field_offset >= INT32_MIN &&
+ field_offset <= INT32_MAX) {
+ i32 ofs;
+ if (!__builtin_add_overflow(base.op.v.ind.ofs, (i32)field_offset, &ofs)) {
+ /* Fold the field offset into the displacement, preserving any index/scale
+ * a preceding `elem` left so `p[i].f` stays one
+ * [base+index*scale+off]. */
+ result = api_op_indirect_indexed(base.op.v.ind.base, base.op.v.ind.index,
+ base.op.v.ind.log2_scale, ofs, field_ty);
+ api_release(g, &base);
+ api_push(g, api_make_lv(result, field_ty));
+ return;
+ }
+ }
+ {
+ Operand base_addr = api_lvalue_addr(g, &base, base_ptr_ty);
+ if (field_offset == 0) {
+ result = base_addr;
+ } else {
+ CGLocal fr = api_alloc_temp_local(g, base_ptr_ty);
+ result = api_op_local(fr, base_ptr_ty);
+ T->binop(T, BO_IADD, result, base_addr,
+ api_op_imm(field_offset, base_ptr_ty));
+ }
+ api_release(g, &base);
+ api_push(
+ g, api_make_lv(api_op_indirect(result.v.local, 0, field_ty), field_ty));
+ }
+}
+
+void kit_cg_field_at(KitCg* g, int64_t byte_offset, KitCgTypeId field_type) {
+ ApiSValue base;
+ if (!g) return;
+ base = api_pop(g);
+ api_cg_field_at_place(g, base, byte_offset, field_type);
+}
+
void kit_cg_field(KitCg* g, uint32_t field_index) {
ApiSValue base;
- CgTarget* T;
KitCgTypeId rec_ty;
KitCgTypeId base_ty;
KitCgTypeId field_ty;
@@ -1222,10 +1478,7 @@ void kit_cg_field(KitCg* g, uint32_t field_index) {
const CgType* rec_info;
const ABIRecordLayout* layout;
u32 field_offset;
- Operand result;
- CGLocal rr;
if (!g) return;
- T = g->target;
base = api_pop(g);
api_ensure_local(g, &base);
base_ty = api_sv_type(&base);
@@ -1251,6 +1504,22 @@ void kit_cg_field(KitCg* g, uint32_t field_index) {
}
field_ty = rec_info->record.fields[field_index].type;
field_offset = layout->fields[field_index].offset;
+ if (api_unevaluated(g)) {
+ ApiSValue sv = api_uneval_place(g, field_ty);
+ if (layout->fields[field_index].bit_width != 0 ||
+ (rec_info->record.fields[field_index].flags & KIT_CG_FIELD_BITFIELD) !=
+ 0) {
+ sv.bitfield.bit_offset = layout->fields[field_index].bit_offset;
+ sv.bitfield.bit_width = layout->fields[field_index].bit_width;
+ sv.bitfield.bit_storage_size = layout->fields[field_index].storage_size;
+ sv.bitfield.bit_signed =
+ rec_info->record.fields[field_index].bit_signed ? 1u : 0u;
+ }
+ api_release(g, &base);
+ api_push(g, sv);
+ api_const_set_top(g, api_const_unknown(field_ty));
+ return;
+ }
if (layout->fields[field_index].bit_width != 0 ||
(rec_info->record.fields[field_index].flags & KIT_CG_FIELD_BITFIELD) !=
0) {
@@ -1276,36 +1545,7 @@ void kit_cg_field(KitCg* g, uint32_t field_index) {
api_push(g, sv);
return;
}
- if (base.op.kind == OPK_GLOBAL) {
- result =
- api_op_global(base.op.v.global.sym,
- base.op.v.global.addend + (i64)field_offset, field_ty);
- api_push(g, api_make_lv(result, field_ty));
- } else if (base.op.kind == OPK_INDIRECT && field_offset <= (u32)INT32_MAX &&
- base.op.v.ind.ofs <= INT32_MAX - (i32)field_offset) {
- /* Fold the field offset into the displacement, preserving any index/scale
- * a preceding `elem` left so `p[i].f` stays one [base+index*scale+off]. */
- result = api_op_indirect_indexed(
- base.op.v.ind.base, base.op.v.ind.index, base.op.v.ind.log2_scale,
- base.op.v.ind.ofs + (i32)field_offset, field_ty);
- api_push(g, api_make_lv(result, field_ty));
- } else {
- Operand base_addr;
- rr = api_alloc_temp_local(g, rec_ptr_ty);
- base_addr = api_op_local(rr, rec_ptr_ty);
- T->addr_of(T, base_addr, base.op);
- api_release(g, &base);
- if (field_offset == 0) {
- result = base_addr;
- } else {
- CGLocal fr = api_alloc_temp_local(g, rec_ptr_ty);
- result = api_op_local(fr, rec_ptr_ty);
- T->binop(T, BO_IADD, result, base_addr,
- api_op_imm((i64)field_offset, rec_ptr_ty));
- }
- api_push(
- g, api_make_lv(api_op_indirect(result.v.local, 0, field_ty), field_ty));
- }
+ api_cg_field_at_place(g, base, (int64_t)field_offset, field_ty);
}
void kit_cg_field_bits(KitCg* g, uint16_t bit_offset, uint16_t bit_width,
diff --git a/src/cg/fold.c b/src/cg/fold.c
@@ -141,11 +141,10 @@ ApiSValue api_make_cmp(KitCg* g, CmpOp op, Operand a, Operand b,
.b = b,
.a_owned = a_owned ? 1u : 0u,
.b_owned = b_owned ? 1u : 0u};
- return (ApiSValue){.kind = SV_CMP,
- .type = result_ty,
- .res = RES_INHERENT,
+ return (ApiSValue){.type = result_ty,
.source_local = KIT_CG_LOCAL_NONE,
- .delayed = d};
+ .delayed = d,
+ .flags = API_SV_PACK(SV_CMP, RES_INHERENT, 0, 0)};
}
CmpOp api_invert_cmp(CmpOp op) {
@@ -204,23 +203,24 @@ CmpOp api_invert_cmp(CmpOp op) {
void api_release_cmp(KitCg* g, ApiSValue* sv) {
api_delayed_free(g, sv->delayed);
sv->delayed = NULL;
- sv->kind = SV_OPERAND;
+ api_sv_set_kind(sv, SV_OPERAND);
}
void api_materialize_cmp_to(KitCg* g, ApiSValue* sv, Operand dst) {
ApiDelayed* d = sv->delayed;
- /* Flag dead-transient operands so the -O0 backend drops them after the compare
- * instead of spilling them at the next barrier (eager dead-operand drop). */
+ /* Flag dead-transient operands so the -O0 backend drops them after the
+ * compare instead of spilling them at the next barrier (eager dead-operand
+ * drop). */
Operand a = api_op_kill_if_dead(g, d->cmp.a, dst);
Operand b = api_op_kill_if_dead(g, d->cmp.b, dst);
g->target->cmp(g->target, d->cmp.op, dst, a, b);
api_delayed_free(g, d);
sv->delayed = NULL;
- sv->kind = SV_OPERAND;
+ api_sv_set_kind(sv, SV_OPERAND);
sv->op = dst;
sv->type = dst.type;
- sv->res = RES_LOCAL;
- sv->lvalue = 0;
+ api_sv_set_res(sv, RES_LOCAL);
+ api_sv_set_lvalue(sv, 0);
}
/* ============================================================
@@ -241,11 +241,10 @@ ApiSValue api_make_arith_unop(KitCg* g, UnOp op, Operand a, KitCgTypeId ty,
.un_op = op,
.a = a,
.a_owned = a_owned ? 1u : 0u};
- return (ApiSValue){.kind = SV_ARITH,
- .type = ty,
- .res = RES_INHERENT,
+ return (ApiSValue){.type = ty,
.source_local = KIT_CG_LOCAL_NONE,
- .delayed = d};
+ .delayed = d,
+ .flags = API_SV_PACK(SV_ARITH, RES_INHERENT, 0, 0)};
}
ApiSValue api_make_arith_binop(KitCg* g, BinOp op, Operand a, Operand b,
@@ -257,17 +256,16 @@ ApiSValue api_make_arith_binop(KitCg* g, BinOp op, Operand a, Operand b,
.b = b,
.a_owned = a_owned ? 1u : 0u,
.b_owned = b_owned ? 1u : 0u};
- return (ApiSValue){.kind = SV_ARITH,
- .type = ty,
- .res = RES_INHERENT,
+ return (ApiSValue){.type = ty,
.source_local = KIT_CG_LOCAL_NONE,
- .delayed = d};
+ .delayed = d,
+ .flags = API_SV_PACK(SV_ARITH, RES_INHERENT, 0, 0)};
}
void api_release_arith(KitCg* g, ApiSValue* sv) {
api_delayed_free(g, sv->delayed);
sv->delayed = NULL;
- sv->kind = SV_OPERAND;
+ api_sv_set_kind(sv, SV_OPERAND);
}
void api_materialize_arith_to(KitCg* g, ApiSValue* sv, Operand dst) {
@@ -277,18 +275,19 @@ void api_materialize_arith_to(KitCg* g, ApiSValue* sv, Operand dst) {
g->target->unop(g->target, d->arith.un_op, dst, a);
} else {
/* Flag dead-transient operands so the -O0 backend drops them after the op
- * instead of spilling them at the next barrier (eager dead-operand drop). */
+ * instead of spilling them at the next barrier (eager dead-operand drop).
+ */
Operand a = api_op_kill_if_dead(g, d->arith.a, dst);
Operand b = api_op_kill_if_dead(g, d->arith.b, dst);
g->target->binop(g->target, d->arith.bin_op, dst, a, b);
}
api_delayed_free(g, d);
sv->delayed = NULL;
- sv->kind = SV_OPERAND;
+ api_sv_set_kind(sv, SV_OPERAND);
sv->op = dst;
sv->type = dst.type;
- sv->res = RES_LOCAL;
- sv->lvalue = 0;
+ api_sv_set_res(sv, RES_LOCAL);
+ api_sv_set_lvalue(sv, 0);
}
int api_arith_rhs_reusable(const ApiSValue* sv) {
@@ -344,8 +343,8 @@ int api_try_strength_reduce(KitCg* g, BinOp* op, KitCgTypeId ty, ApiSValue* a,
int a_imm, b_imm;
if (!g || !op || !a || !b) return 0;
if (!api_foldable_int_type(g->c, ty, &width)) return 0;
- a_imm = a->kind == SV_OPERAND && a->op.kind == OPK_IMM;
- b_imm = b->kind == SV_OPERAND && b->op.kind == OPK_IMM;
+ a_imm = api_sv_kind(a) == SV_OPERAND && a->op.kind == OPK_IMM;
+ b_imm = api_sv_kind(b) == SV_OPERAND && b->op.kind == OPK_IMM;
switch (*op) {
case BO_IMUL: {
/* Both-imm is constant-folded before we get here; need exactly one, and
@@ -424,20 +423,21 @@ int api_try_collapse_binop_identity(KitCg* g, BinOp op, KitCgTypeId ty,
u64 av = 0;
u64 bv = 0;
if (!api_foldable_int_type(g->c, ty, &width)) return 0;
- if (a->kind == SV_OPERAND && a->op.kind == OPK_IMM)
+ if (api_sv_kind(a) == SV_OPERAND && a->op.kind == OPK_IMM)
av = api_mask_width((u64)a->op.v.imm, width);
- if (b->kind == SV_OPERAND && b->op.kind == OPK_IMM)
+ if (api_sv_kind(b) == SV_OPERAND && b->op.kind == OPK_IMM)
bv = api_mask_width((u64)b->op.v.imm, width);
- if (b->kind == SV_OPERAND && b->op.kind == OPK_IMM && a->kind == SV_OPERAND &&
- a->op.kind != OPK_IMM && api_op_is_int_identity(g, op, ty, b->op.v.imm)) {
+ if (api_sv_kind(b) == SV_OPERAND && b->op.kind == OPK_IMM &&
+ api_sv_kind(a) == SV_OPERAND && a->op.kind != OPK_IMM &&
+ api_op_is_int_identity(g, op, ty, b->op.v.imm)) {
*out = api_make_sv_with_local_ownership(
a->op, ty, api_sv_owns_operand_local(a, &a->op));
- a->res = RES_INHERENT;
+ api_sv_set_res(a, RES_INHERENT);
return 1;
}
- if (b->kind == SV_OPERAND && b->op.kind == OPK_IMM && a->kind == SV_OPERAND &&
- a->op.kind != OPK_IMM &&
+ if (api_sv_kind(b) == SV_OPERAND && b->op.kind == OPK_IMM &&
+ api_sv_kind(a) == SV_OPERAND && a->op.kind != OPK_IMM &&
(op == BO_SREM || op == BO_UREM || op == BO_IMUL || op == BO_AND ||
op == BO_OR)) {
if ((op == BO_SREM || op == BO_UREM) && bv == 1) {
@@ -454,18 +454,19 @@ int api_try_collapse_binop_identity(KitCg* g, BinOp op, KitCgTypeId ty,
return 1;
}
}
- if (a->kind == SV_OPERAND && a->op.kind == OPK_IMM && b->kind == SV_OPERAND &&
- b->op.kind != OPK_IMM &&
+ if (api_sv_kind(a) == SV_OPERAND && a->op.kind == OPK_IMM &&
+ api_sv_kind(b) == SV_OPERAND && b->op.kind != OPK_IMM &&
(op == BO_IADD || op == BO_IMUL || op == BO_OR || op == BO_XOR ||
op == BO_AND) &&
api_op_is_int_identity(g, op, ty, a->op.v.imm)) {
*out = api_make_sv_with_local_ownership(
b->op, ty, api_sv_owns_operand_local(b, &b->op));
- b->res = RES_INHERENT;
+ api_sv_set_res(b, RES_INHERENT);
return 1;
}
- if (a->kind == SV_OPERAND && a->op.kind == OPK_IMM && b->kind == SV_OPERAND &&
- b->op.kind != OPK_IMM && (op == BO_IMUL || op == BO_AND || op == BO_OR)) {
+ if (api_sv_kind(a) == SV_OPERAND && a->op.kind == OPK_IMM &&
+ api_sv_kind(b) == SV_OPERAND && b->op.kind != OPK_IMM &&
+ (op == BO_IMUL || op == BO_AND || op == BO_OR)) {
if ((op == BO_IMUL || op == BO_AND) && av == 0) {
*out = api_make_sv(api_op_imm(0, ty), ty);
return 1;
@@ -484,9 +485,10 @@ int api_try_fold_arith_chain(KitCg* g, BinOp op, KitCgTypeId ty, ApiSValue* a,
i64 folded;
BinOp result_op;
ApiDelayed* ad;
- if (a->kind != SV_ARITH || a->delayed->arith.kind != API_DELAYED_BINOP ||
+ if (api_sv_kind(a) != SV_ARITH ||
+ a->delayed->arith.kind != API_DELAYED_BINOP ||
a->delayed->arith.a.kind != OPK_LOCAL ||
- a->delayed->arith.b.kind != OPK_IMM || b->kind != SV_OPERAND ||
+ a->delayed->arith.b.kind != OPK_IMM || api_sv_kind(b) != SV_OPERAND ||
b->op.kind != OPK_IMM) {
return 0;
}
@@ -524,23 +526,23 @@ int api_try_fold_arith_chain(KitCg* g, BinOp op, KitCgTypeId ty, ApiSValue* a,
}
break;
case BO_XOR:
- if (op != BO_XOR || !api_try_fold_int_binop(g, BO_XOR, ty,
- ad->arith.b.v.imm,
- b->op.v.imm, &folded))
+ if (op != BO_XOR ||
+ !api_try_fold_int_binop(g, BO_XOR, ty, ad->arith.b.v.imm, b->op.v.imm,
+ &folded))
return 0;
result_op = BO_XOR;
break;
case BO_AND:
- if (op != BO_AND || !api_try_fold_int_binop(g, BO_AND, ty,
- ad->arith.b.v.imm,
- b->op.v.imm, &folded))
+ if (op != BO_AND ||
+ !api_try_fold_int_binop(g, BO_AND, ty, ad->arith.b.v.imm, b->op.v.imm,
+ &folded))
return 0;
result_op = BO_AND;
break;
case BO_OR:
- if (op != BO_OR || !api_try_fold_int_binop(g, BO_OR, ty,
- ad->arith.b.v.imm, b->op.v.imm,
- &folded))
+ if (op != BO_OR ||
+ !api_try_fold_int_binop(g, BO_OR, ty, ad->arith.b.v.imm, b->op.v.imm,
+ &folded))
return 0;
result_op = BO_OR;
break;
@@ -557,7 +559,8 @@ int api_try_fold_arith_chain(KitCg* g, BinOp op, KitCgTypeId ty, ApiSValue* a,
}
/* Chain-folds into an updated delayed binop. Move a's payload to out (a
* pointer transfer, not a deep copy) and null a's pointer so the caller's
- * api_release reclaims nothing — out owns the payload until it materializes. */
+ * api_release reclaims nothing — out owns the payload until it materializes.
+ */
ad->arith.bin_op = result_op;
ad->arith.b.v.imm = folded;
*out = *a;
@@ -567,7 +570,7 @@ int api_try_fold_arith_chain(KitCg* g, BinOp op, KitCgTypeId ty, ApiSValue* a,
int api_try_fold_unary_chain(ApiSValue* a, UnOp op, KitCgTypeId ty,
ApiSValue* out) {
- if (op != UO_BNOT || a->kind != SV_ARITH ||
+ if (op != UO_BNOT || api_sv_kind(a) != SV_ARITH ||
a->delayed->arith.kind != API_DELAYED_UNOP ||
a->delayed->arith.un_op != UO_BNOT ||
a->delayed->arith.a.kind != OPK_LOCAL) {
@@ -575,9 +578,8 @@ int api_try_fold_unary_chain(ApiSValue* a, UnOp op, KitCgTypeId ty,
}
/* out borrows a's input operand (a plain value); a keeps its payload for the
* caller's api_release to reclaim. */
- *out =
- api_make_sv_with_local_ownership(a->delayed->arith.a, ty,
- a->delayed->arith.a_owned);
+ *out = api_make_sv_with_local_ownership(a->delayed->arith.a, ty,
+ a->delayed->arith.a_owned);
return 1;
}
diff --git a/src/cg/internal.h b/src/cg/internal.h
@@ -9,6 +9,7 @@
#include "abi/abi.h"
#include "asm/asm.h"
+#include "cg/cgtarget.h"
#include "cg/type.h"
#include "core/arena.h"
#include "core/heap.h"
@@ -16,7 +17,6 @@
#include "core/segvec.h"
#include "core/slice.h"
#include "core/strbuf.h"
-#include "cg/cgtarget.h"
#include "debug/debug.h"
#include "obj/obj.h"
@@ -102,17 +102,70 @@ typedef struct ApiSValue {
* A pointer (not the 64-byte union inline) keeps the node small; only the
* SV_CMP / SV_ARITH producers in fold.c allocate one. */
ApiDelayed* delayed;
+ const void* lang_type; /* opaque frontend-owned slot fact */
KitCgTypeId type;
- u8 kind;
- u8 res;
- u8 pinned;
- u8 lvalue;
KitCgLocal source_local;
ApiBitField bitfield; /* bit_width != 0 marks a bit-field PLACE subkind */
+ u16 lang_flags; /* frontend-defined flags; CG only copies/clears */
+ u16 flags; /* packed ApiSValueKind/SResidency/pinned/lvalue */
} ApiSValue;
+typedef struct ApiConstValue {
+ KitCgConstInt value;
+ KitCgTypeId type;
+} ApiConstValue;
+
#define API_CG_STACK_INITIAL 16u
+#define API_SV_KIND_SHIFT 0u
+#define API_SV_RES_SHIFT 2u
+#define API_SV_PINNED_SHIFT 4u
+#define API_SV_LVALUE_SHIFT 5u
+#define API_SV_KIND_MASK 0x3u
+#define API_SV_RES_MASK 0x3u
+#define API_SV_BOOL_MASK 0x1u
+#define API_SV_PACK(kind_, res_, pinned_, lvalue_) \
+ ((u16)((((u16)(kind_) & API_SV_KIND_MASK) << API_SV_KIND_SHIFT) | \
+ (((u16)(res_) & API_SV_RES_MASK) << API_SV_RES_SHIFT) | \
+ (((u16)(pinned_) & API_SV_BOOL_MASK) << API_SV_PINNED_SHIFT) | \
+ (((u16)(lvalue_) & API_SV_BOOL_MASK) << API_SV_LVALUE_SHIFT)))
+
+static inline ApiSValueKind api_sv_kind(const ApiSValue* sv) {
+ return (ApiSValueKind)((sv->flags >> API_SV_KIND_SHIFT) & API_SV_KIND_MASK);
+}
+
+static inline void api_sv_set_kind(ApiSValue* sv, ApiSValueKind kind) {
+ sv->flags = (u16)((sv->flags & ~(API_SV_KIND_MASK << API_SV_KIND_SHIFT)) |
+ (((u16)kind & API_SV_KIND_MASK) << API_SV_KIND_SHIFT));
+}
+
+static inline SResidency api_sv_res(const ApiSValue* sv) {
+ return (SResidency)((sv->flags >> API_SV_RES_SHIFT) & API_SV_RES_MASK);
+}
+
+static inline void api_sv_set_res(ApiSValue* sv, SResidency res) {
+ sv->flags = (u16)((sv->flags & ~(API_SV_RES_MASK << API_SV_RES_SHIFT)) |
+ (((u16)res & API_SV_RES_MASK) << API_SV_RES_SHIFT));
+}
+
+static inline int api_sv_pinned(const ApiSValue* sv) {
+ return (int)((sv->flags >> API_SV_PINNED_SHIFT) & API_SV_BOOL_MASK);
+}
+
+static inline void api_sv_set_pinned(ApiSValue* sv, int pinned) {
+ sv->flags = (u16)((sv->flags & ~(API_SV_BOOL_MASK << API_SV_PINNED_SHIFT)) |
+ (((u16)pinned & API_SV_BOOL_MASK) << API_SV_PINNED_SHIFT));
+}
+
+static inline int api_sv_lvalue_flag(const ApiSValue* sv) {
+ return (int)((sv->flags >> API_SV_LVALUE_SHIFT) & API_SV_BOOL_MASK);
+}
+
+static inline void api_sv_set_lvalue(ApiSValue* sv, int lvalue) {
+ sv->flags = (u16)((sv->flags & ~(API_SV_BOOL_MASK << API_SV_LVALUE_SHIFT)) |
+ (((u16)lvalue & API_SV_BOOL_MASK) << API_SV_LVALUE_SHIFT));
+}
+
/* Largest scalar the codegen lowers as a native (lock-free) atomic. All
* current targets — aa64, x64, rv64, wasm32 — provide 8-byte (i64-width)
* atomics, so this is both the legality ceiling and the lock-free ceiling.
@@ -139,15 +192,25 @@ typedef struct ApiCgScope {
* nparams==0 reproduces the old single-result path byte-for-byte. */
CGLocal* result_locals;
KitCgTypeId* result_types;
+ const void** result_lang_types;
+ u16* result_lang_flags;
CGLocal* param_locals;
KitCgTypeId* param_types;
+ const void** param_lang_types;
+ u16* param_lang_flags;
u32 nresults;
u32 nparams;
CGLocal result_locals_inl[API_CG_SCOPE_SIG_INLINE];
KitCgTypeId result_types_inl[API_CG_SCOPE_SIG_INLINE];
+ const void* result_lang_types_inl[API_CG_SCOPE_SIG_INLINE];
+ u16 result_lang_flags_inl[API_CG_SCOPE_SIG_INLINE];
CGLocal param_locals_inl[API_CG_SCOPE_SIG_INLINE];
KitCgTypeId param_types_inl[API_CG_SCOPE_SIG_INLINE];
- u32* heap_block; /* non-NULL when arity spilled to heap; freed at scope_end */
+ const void* param_lang_types_inl[API_CG_SCOPE_SIG_INLINE];
+ u16 param_lang_flags_inl[API_CG_SCOPE_SIG_INLINE];
+ u32* heap_block; /* non-NULL when u32 arity vectors spilled to heap */
+ const void** heap_lang_types_block;
+ u16* heap_lang_flags_block;
u32 generation;
u8 active;
u8 pad[3];
@@ -202,8 +265,10 @@ struct KitCg {
u8 lifecycle_pad[2];
ApiSValue* stack;
+ ApiConstValue* const_stack;
u32 sp;
u32 cap;
+ u32 unevaluated_depth;
/* -O0 transient liveness. local_refs[h] = number of live value-stack entries
* that reference CGLocal handle h (maintained at api_push/api_pop, plus a
@@ -212,20 +277,21 @@ struct KitCg {
* backend recycles (Fix A) or coalesces. The count is used only as a
* fast-reject prefilter: a transient is treated as dead solely when the count
* reads 0 AND a confirming stack scan (api_temp_dead) agrees, so any reseat
- * gap costs at most a missed optimization, never a miscompile. Sized on demand
- * by handle; cleared per function. NULL/0 until first use. */
+ * gap costs at most a missed optimization, never a miscompile. Sized on
+ * demand by handle; cleared per function. NULL/0 until first use. */
u32* local_refs; /* live value-stack reference count per handle */
u32* local_temp_gen; /* == func_gen iff the handle is this function's temp */
u32 local_track_cap;
- u32 func_gen; /* bumped each function; stamps temps without O(cap) clears */
- u8 coalesce; /* -O0 copy/dup coalescing + finer reclaim enabled */
+ u32 func_gen; /* bumped each function; stamps temps without O(cap) clears */
+ u8 coalesce; /* -O0 copy/dup coalescing + finer reclaim enabled */
u8 coalesce_known; /* coalesce resolved from env (once) */
u8 coalesce_pad[2];
/* Off-node pool for SV_CMP / SV_ARITH delayed payloads. The arena is reset
* per function (api_delayed_reset at func_begin, where the stack is also
* dropped); delayed_free is an intrusive freelist that reuses payloads within
- * a function so the arena only grows to the live delayed-value working set. */
+ * a function so the arena only grows to the live delayed-value working set.
+ */
Arena delayed_arena;
ApiDelayed* delayed_free;
u8 delayed_arena_init;
@@ -375,7 +441,10 @@ ApiCgScope* api_scope_from_handle(KitCg* g, KitCgScope scope, int require_top,
int api_scope_has_result(const ApiCgScope* s);
void api_scope_store_results(KitCg* g, ApiCgScope* s);
void api_scope_push_results(KitCg* g, ApiCgScope* s);
-KitCgScope kit_cg_scope_begin(KitCg* g, KitCgTypeId result_type);
+KitCgScope kit_cg_scope_begin(KitCg* g);
+KitCgScope kit_cg_scope_begin_value(KitCg* g, KitCgTypeId result_type);
+KitCgScope kit_cg_block_begin(KitCg* g);
+KitCgScope kit_cg_block_begin_value(KitCg* g, KitCgTypeId result_type);
KitCgScope kit_cg_scope_begin_sig(KitCg* g, const KitCgScopeSig* sig);
KitCgScope kit_cg_block_begin_sig(KitCg* g, const KitCgScopeSig* sig);
void kit_cg_scope_store_params(KitCg* g, KitCgScope scope);
@@ -444,10 +513,17 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access);
void kit_cg_addr(KitCg* g);
void kit_cg_deref(KitCg* g, int64_t offset);
void kit_cg_store(KitCg* g, KitCgMemAccess access);
+void kit_cg_store_keep(KitCg* g, KitCgMemAccess access);
+void kit_cg_field_at(KitCg* g, int64_t byte_offset, KitCgTypeId field_type);
void kit_cg_dup(KitCg* g);
void kit_cg_dup2(KitCg* g);
void kit_cg_swap(KitCg* g);
void kit_cg_drop(KitCg* g);
+KitCgSlotInfo kit_cg_slot_info(KitCg* g, uint32_t depth_from_top);
+void kit_cg_retag_top(KitCg* g, const void* lang_type, uint16_t lang_flags);
+void kit_cg_retag_at(KitCg* g, uint32_t depth_from_top, const void* lang_type,
+ uint16_t lang_flags);
+void kit_cg_set_top_flags(KitCg* g, uint16_t set, uint16_t clear);
uint32_t kit_cg_stack_depth(KitCg* g);
int kit_cg_top_const_int(KitCg* g, int64_t* out_value);
void kit_cg_rot3(KitCg* g);
@@ -507,6 +583,28 @@ MemAccess api_mem_for_bitfield(KitCg* g, const ApiSValue* sv,
void api_stack_grow(KitCg* g, u32 want);
void api_push(KitCg* g, ApiSValue v);
ApiSValue api_pop(KitCg* g);
+int api_unevaluated(KitCg* g);
+ApiSValue api_uneval_value(KitCg* g, KitCgTypeId type);
+ApiSValue api_uneval_place(KitCg* g, KitCgTypeId type);
+ApiConstValue api_const_unknown(KitCgTypeId type);
+ApiConstValue api_const_from_sv(KitCg* g, const ApiSValue* sv);
+ApiConstValue api_const_at(KitCg* g, u32 depth);
+void api_const_set_top(KitCg* g, ApiConstValue value);
+void api_const_set_at(KitCg* g, u32 depth, ApiConstValue value);
+void api_const_copy_top_from(KitCg* g, ApiConstValue value);
+ApiConstValue api_const_for_push(KitCg* g, KitCgTypeId type,
+ const KitCgConstInt* value);
+int api_const_fold_binop(KitCg* g, BinOp op, KitCgTypeId type, ApiConstValue a,
+ ApiConstValue b, u32 flags, ApiConstValue* out);
+int api_const_fold_unop(KitCg* g, UnOp op, KitCgTypeId type, ApiConstValue a,
+ u32 flags, ApiConstValue* out);
+int api_const_fold_cmp(KitCg* g, CmpOp op, ApiConstValue a, ApiConstValue b,
+ ApiConstValue* out);
+int api_const_fold_convert(KitCg* g, ConvKind ck, KitCgTypeId src_type,
+ KitCgTypeId dst_type, ApiConstValue in,
+ ApiConstValue* out);
+ApiConstValue api_const_int_result(KitCg* g, KitCgTypeId type, u64 lo, u64 hi,
+ int is_signed);
CGLocal api_local_of_sv(const ApiSValue* sv);
void api_set_owned_local(ApiSValue* sv, CGLocal r);
KitCgTypeId api_owned_local_type(KitCg* g, const ApiSValue* sv);
@@ -527,22 +625,23 @@ Operand api_force_local(KitCg* g, ApiSValue* v, KitCgTypeId ty);
Operand api_force_local_unless_imm(KitCg* g, ApiSValue* v, KitCgTypeId ty);
void api_release(KitCg* g, ApiSValue* sv);
-/* -O0 transient liveness (see KitCg.local_refs). api_coalesce_on returns whether
- * the copy/dup coalescing + finer-reclaim mechanisms are enabled this run.
- * api_temp_dead reports whether transient `local` is provably dead right now (no
- * live value-stack entry references it): count==0 confirmed by a stack scan.
- * api_reseat_{begin,end} bracket an in-place change to a stack entry's operand so
- * its references are re-accounted. */
+/* -O0 transient liveness (see KitCg.local_refs). api_coalesce_on returns
+ * whether the copy/dup coalescing + finer-reclaim mechanisms are enabled this
+ * run. api_temp_dead reports whether transient `local` is provably dead right
+ * now (no live value-stack entry references it): count==0 confirmed by a stack
+ * scan. api_reseat_{begin,end} bracket an in-place change to a stack entry's
+ * operand so its references are re-accounted. */
int api_coalesce_on(KitCg* g);
int api_temp_dead(KitCg* g, CGLocal local);
-/* Flag OP for the -O0 backend's eager dead-operand drop: when coalescing is on and
- * OP names a transient that api_temp_dead confirms is dead after the consuming op,
- * AND OP is distinct from that op's destination DST (so it is not the result being
- * written), return OP with OPK_FLAG_KILL set; otherwise return OP unchanged. The
- * NativeDirectTarget then drops OP's live cache register after the op (no spill, no
- * reload) instead of leaving it resident to be flush-stored at the next barrier.
- * Pass a non-OPK_LOCAL operand for DST (e.g. the store's memory place) when the op
- * has no local destination — the self-guard is then a no-op. */
+/* Flag OP for the -O0 backend's eager dead-operand drop: when coalescing is on
+ * and OP names a transient that api_temp_dead confirms is dead after the
+ * consuming op, AND OP is distinct from that op's destination DST (so it is not
+ * the result being written), return OP with OPK_FLAG_KILL set; otherwise return
+ * OP unchanged. The NativeDirectTarget then drops OP's live cache register
+ * after the op (no spill, no reload) instead of leaving it resident to be
+ * flush-stored at the next barrier. Pass a non-OPK_LOCAL operand for DST (e.g.
+ * the store's memory place) when the op has no local destination — the
+ * self-guard is then a no-op. */
Operand api_op_kill_if_dead(KitCg* g, Operand op, Operand dst);
void api_reseat_begin(KitCg* g, const ApiSValue* sv);
void api_reseat_end(KitCg* g, const ApiSValue* sv);
@@ -559,6 +658,8 @@ void api_store_f128_bytes(KitCg* g, CGLocal local, KitCgTypeId ty,
const u8 bytes[16]);
void api_wide16_sext_imm_bytes(KitCg* g, i64 imm, u8 bytes[16]);
ApiSValue api_make_wide16_int_const(KitCg* g, i64 value, KitCgTypeId ty);
+ApiSValue api_make_wide16_int_const_bits(KitCg* g, u64 lo, u64 hi,
+ KitCgTypeId ty);
void api_encode_binary128_from_double(KitCg* g, double value, u8 out[16]);
ApiSValue api_make_f128_const(KitCg* g, double value, KitCgTypeId ty);
ApiSValue api_wide16_materialize_lvalue(KitCg* g, ApiSValue* v, KitCgTypeId ty);
diff --git a/src/cg/local.c b/src/cg/local.c
@@ -75,7 +75,9 @@ KitCgLocal kit_cg_local(KitCg* g, KitCgTypeId type, KitCgLocalAttrs attrs) {
desc.align = attrs.align ? attrs.align : abi_cg_alignof(g->c->abi, type);
if (api_local_requires_memory(g, ty, attrs))
desc.flags |= CG_LOCAL_MEMORY_REQUIRED;
- if (g->target->local)
+ if (api_unevaluated(g)) {
+ storage = CG_LOCAL_NONE;
+ } else if (g->target->local)
storage = g->target->local(g->target, &desc);
else
storage = api_frame_local_storage(g, &desc);
@@ -121,7 +123,8 @@ KitCgLocal kit_cg_param(KitCg* g, uint32_t index, KitCgTypeId type,
if (api_local_requires_memory(g, ty, attrs))
pd.flags |= CG_LOCAL_MEMORY_REQUIRED;
pd.loc = g->cur_loc;
- storage = g->target->param(g->target, &pd);
+ storage =
+ api_unevaluated(g) ? CG_LOCAL_NONE : g->target->param(g->target, &pd);
rec = &g->locals[g->nlocals++];
memset(rec, 0, sizeof *rec);
diff --git a/src/cg/memory.c b/src/cg/memory.c
@@ -2,23 +2,56 @@
void kit_cg_push_int(KitCg* g, uint64_t value, KitCgTypeId type) {
KitCgTypeId ty;
+ ApiConstValue cv;
if (!g) return;
ty = resolve_type(g->c, type);
if (!ty) return;
+ cv = api_const_int_result(g, ty, value, 0, 0);
+ if (api_unevaluated(g)) {
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, cv);
+ return;
+ }
/* A 16-byte scalar immediate cannot be represented by the 64-bit op.v.imm
* alone; materialize it into addressable storage with both lanes
* sign-extended so no downstream consumer sees an undefined high half. */
if (api_is_wide16_scalar_type(g->c, ty)) {
api_push(g, api_make_wide16_int_const(g, (i64)value, ty));
+ api_const_set_top(g, cv);
return;
}
/* Split-lane 8-byte int: the 64-bit value fits in op.v.imm, but the value is
* memory-resident, so materialize it as two 32-bit lanes. */
if (api_is_wide8_scalar_type(g->c, ty)) {
api_push(g, api_make_wide8_int_const(g, (i64)value, ty));
+ api_const_set_top(g, cv);
return;
}
api_push(g, api_make_sv(api_op_imm((i64)value, ty), ty));
+ api_const_set_top(g, cv);
+}
+
+void kit_cg_push_const_int(KitCg* g, KitCgTypeId type,
+ const KitCgConstInt* value) {
+ KitCgTypeId ty;
+ ApiConstValue cv;
+ if (!g) return;
+ ty = resolve_type(g->c, type);
+ if (!ty) return;
+ cv = api_const_for_push(g, ty, value);
+ if (api_unevaluated(g)) {
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, cv);
+ return;
+ }
+ if (cv.value.known && cv.value.width > 64u) {
+ api_push(g,
+ api_make_wide16_int_const_bits(g, cv.value.lo, cv.value.hi, ty));
+ api_const_set_top(g, cv);
+ return;
+ }
+ kit_cg_push_int(g, cv.value.known ? cv.value.lo : 0, ty);
+ api_const_set_top(g, cv);
}
void kit_cg_push_float(KitCg* g, double value, KitCgTypeId type) {
@@ -35,6 +68,11 @@ void kit_cg_push_float(KitCg* g, double value, KitCgTypeId type) {
if (!g) return;
ty = resolve_type(g->c, type);
if (!ty) return;
+ if (api_unevaluated(g)) {
+ api_push(g, api_uneval_value(g, ty));
+ api_const_set_top(g, api_const_unknown(ty));
+ return;
+ }
if (api_is_f128_type(g->c, ty)) {
api_push(g, api_make_f128_const(g, value, ty));
return;
@@ -67,10 +105,13 @@ void kit_cg_push_float(KitCg* g, double value, KitCgTypeId type) {
void kit_cg_push_null(KitCg* g, KitCgTypeId ptr_type) {
KitCgTypeId ty;
+ ApiConstValue cv;
if (!g) return;
ty = resolve_type(g->c, ptr_type);
if (!ty) return;
+ cv = api_const_int_result(g, ty, 0, 0, 0);
api_push(g, api_make_sv(api_op_imm(0, ty), ty));
+ api_const_set_top(g, cv);
}
static int api_const_data_can_defer(const KitCg* g) {
@@ -119,6 +160,7 @@ KitCgSym kit_cg_const_data(KitCg* g, const uint8_t* data, size_t len,
KitCgDecl attrs;
int defer;
if (!g) return KIT_CG_SYM_NONE;
+ if (api_unevaluated(g)) return KIT_CG_SYM_NONE;
c = g->c;
ob = g->obj;
pty = resolve_type(c, pointee_type);
@@ -156,6 +198,7 @@ KitCgSym kit_cg_const_data(KitCg* g, const uint8_t* data, size_t len,
void api_push_local_lvalue(KitCg* g, CGLocal local, KitCgTypeId type) {
if (!g) return;
api_push(g, api_make_lv(api_op_local(local, type), type));
+ api_const_set_top(g, api_const_unknown(type));
}
void api_push_source_local_lvalue(KitCg* g, KitCgLocal source_local,
@@ -187,6 +230,11 @@ void kit_cg_push_symbol_addr(KitCg* g, KitCgSym sym, int64_t addend) {
ty = api_sym_type(g, sym);
if (!ty) ty = builtin_id(KIT_CG_BUILTIN_VOID);
ptr_ty = cg_type_ptr_to(g->c, ty);
+ if (api_unevaluated(g)) {
+ api_push(g, api_uneval_value(g, ptr_ty));
+ api_const_set_top(g, api_const_unknown(ptr_ty));
+ return;
+ }
if (api_sym_is_tls(g, sym)) {
CGLocal r = api_alloc_temp_local(g, ptr_ty);
Operand dst = api_op_local(r, ptr_ty);
@@ -226,6 +274,18 @@ static Operand place_operand_for_access(Operand op, KitCgTypeId access_ty) {
}
}
+static KitCgMemAccess api_load_access_with_hints(KitCg* g,
+ KitCgMemAccess access,
+ KitCgTypeId access_ty,
+ int is_bitfield) {
+ if (!is_bitfield && (access.flags & KIT_CG_MEM_SOURCE_SIGNED) &&
+ cg_type_is_int(g->c, access_ty) &&
+ (u32)api_mem_type_size(g, access_ty, "load") < 4u) {
+ access.flags |= KIT_CG_MEM_SEXT_LOAD;
+ }
+ return access;
+}
+
/* Load a VALUE from the PLACE on TOS. The place encodes the full address (built
* by push_local / deref / field / elem); there is no EA rider. Strict: the
* operand must be a PLACE — a pointer VALUE must be `deref`'d first. */
@@ -262,6 +322,8 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access) {
"size %u, lvalue size %u",
(unsigned)access_size, (unsigned)lvalue_size);
}
+ base.lang_type = NULL;
+ base.lang_flags = 0;
api_push(g, base);
return;
}
@@ -272,6 +334,14 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access) {
access_ty = ty;
if (!is_bitfield) api_require_scalar_mem_type(g, "load", access_ty);
+ access = api_load_access_with_hints(g, access, access_ty, is_bitfield);
+
+ if (api_unevaluated(g)) {
+ api_release(g, &base);
+ api_push(g, api_uneval_value(g, access_ty));
+ api_const_set_top(g, api_const_unknown(access_ty));
+ return;
+ }
/* Source-local constant load. */
if (!is_bitfield && base.source_local != KIT_CG_LOCAL_NONE &&
@@ -286,14 +356,15 @@ void kit_cg_load(KitCg* g, KitCgMemAccess access) {
* (api_type_pred_bits also warms the entry's unalias cache), then read the
* aggregate bit and the unalias terminal locally instead of re-decoding. */
if (!is_bitfield && base.source_local != KIT_CG_LOCAL_NONE &&
- base.op.kind == OPK_LOCAL && !api_sv_local_storage_is_aggregate(g, &base)) {
+ base.op.kind == OPK_LOCAL &&
+ !api_sv_local_storage_is_aggregate(g, &base)) {
KitCgTypeId base_ty = api_sv_type(&base);
u8 base_bits = api_type_pred_bits(g->c, base_ty);
u8 ty_bits = api_type_pred_bits(g->c, ty);
if (!(base_bits & API_PRED_AGGREGATE) && !(ty_bits & API_PRED_AGGREGATE) &&
api_unalias_type(g->c, base_ty) == api_unalias_type(g->c, ty)) {
- base.lvalue = 0;
- base.res = RES_FIXED_LOCAL;
+ api_sv_set_lvalue(&base, 0);
+ api_sv_set_res(&base, RES_FIXED_LOCAL);
api_push(g, base);
return;
}
@@ -344,9 +415,15 @@ void kit_cg_deref(KitCg* g, int64_t offset) {
}
pointee = cg_type_pointee(g->c, pty);
if (!pointee) pointee = builtin_id(KIT_CG_BUILTIN_VOID);
+ if (api_unevaluated(g)) {
+ api_release(g, &v);
+ api_push(g, api_uneval_place(g, pointee));
+ api_const_set_top(g, api_const_unknown(pointee));
+ return;
+ }
/* A symbol address derefs to a global place, preserving direct
* (PC-relative/absolute) addressing rather than materializing the address. */
- if (v.kind == SV_OPERAND && v.op.kind == OPK_GLOBAL) {
+ if (api_sv_kind(&v) == SV_OPERAND && v.op.kind == OPK_GLOBAL) {
api_push(g,
api_make_lv(api_op_global(v.op.v.global.sym,
v.op.v.global.addend + offset, pointee),
@@ -374,16 +451,35 @@ void kit_cg_addr(KitCg* g) {
pty = cg_type_ptr_to(g->c, api_sv_type(&v));
if (v.source_local != KIT_CG_LOCAL_NONE)
api_local_const_address_taken(g, v.source_local);
+ if (api_unevaluated(g)) {
+ api_release(g, &v);
+ api_push(g, api_uneval_value(g, pty));
+ api_const_set_top(g, api_const_unknown(pty));
+ return;
+ }
dst = api_lvalue_addr(g, &v, pty);
api_release(g, &v);
api_push(g, api_make_sv(dst, pty));
}
-/* Store the VALUE on TOS into the PLACE beneath it. Stack: [place, value] ->
- * []. The place encodes the full address; there is no EA rider. Strict: the
- * destination must be a PLACE — a pointer VALUE must be `deref`'d first. */
-void kit_cg_store(KitCg* g, KitCgMemAccess access) {
+static void api_push_kept_store_value(KitCg* g, const ApiSValue* rv,
+ Operand src, KitCgTypeId ty,
+ ApiConstValue cv) {
+ ApiSValue out = api_make_sv_with_local_ownership(
+ src, ty, api_sv_owns_operand_local(rv, &src));
+ out.lang_type = rv->lang_type;
+ out.lang_flags = rv->lang_flags;
+ api_push(g, out);
+ api_const_copy_top_from(g, cv);
+}
+
+/* Store the VALUE on TOS into the PLACE beneath it. keep_result selects
+ * [place,value] -> [value] for assignment expressions without forcing delayed
+ * RHS values before the scalar-local store fast path can use them. */
+static void api_cg_store_impl(KitCg* g, KitCgMemAccess access,
+ int keep_result) {
ApiSValue base, rv;
+ ApiConstValue rv_const;
CgTarget* T;
KitCgTypeId ty;
KitCgTypeId access_ty;
@@ -397,6 +493,7 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
if (access.flags & KIT_CG_MEM_VOLATILE) api_local_const_memory_boundary(g);
/* Stack: [base, value] - pop value, then base. */
+ rv_const = api_const_at(g, 0);
rv = api_pop(g);
base = api_pop(g);
is_lvalue = api_is_lvalue_sv(&base);
@@ -410,6 +507,11 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
ty = api_mem_access_type(g, access, api_sv_type(&base), "store");
access_ty = ty;
+ if (api_unevaluated(g)) {
+ api_release(g, &base);
+ api_release(g, &rv);
+ return;
+ }
/* Aggregate store: memcpy through the source place. `ty` is stable here, so
* decode its aggregate predicate once and reuse it below; likewise decode the
@@ -422,10 +524,10 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
int src_ptr_rvalue;
AggregateAccess agg;
u32 src_size;
- u32 dst_size = ty_is_agg ? api_mem_type_size(g, ty, "store")
- : api_mem_type_size(g, api_sv_type(&base), "store");
- u32 access_size =
- ty_is_agg ? api_mem_type_size(g, ty, "store") : dst_size;
+ u32 dst_size = ty_is_agg
+ ? api_mem_type_size(g, ty, "store")
+ : api_mem_type_size(g, api_sv_type(&base), "store");
+ u32 access_size = ty_is_agg ? api_mem_type_size(g, ty, "store") : dst_size;
src_ptr_rvalue = !api_is_lvalue_sv(&rv) && (rv_bits & API_PRED_PTR);
src_size = src_ptr_rvalue ? access_size
: api_mem_type_size(g, api_sv_type(&rv), "store");
@@ -458,6 +560,11 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
agg.align = access.align ? access.align : abi_cg_alignof(g->c->abi, ty);
T->copy_bytes(T, dst_addr, src_addr, agg);
api_release(g, &base);
+ if (keep_result) {
+ api_push(g, rv);
+ api_const_copy_top_from(g, rv_const);
+ return;
+ }
api_release(g, &rv);
return;
}
@@ -480,20 +587,20 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
}
/* Does this store land a scalar value straight into a local's own storage
- * (a plain `x = <expr>` / `int x = <expr>`, not bit-field/aggregate/indirect)?
- * Single-decode: `ty`'s aggregate bit was already taken (ty_is_agg); decode
- * base's id once for both its aggregate bit and its unalias terminal. */
- int scalar_local_place = !is_bitfield &&
- base.source_local != KIT_CG_LOCAL_NONE &&
- base.op.kind == OPK_LOCAL &&
- !api_sv_local_storage_is_aggregate(g, &base) &&
- !ty_is_agg;
+ * (a plain `x = <expr>` / `int x = <expr>`, not
+ * bit-field/aggregate/indirect)? Single-decode: `ty`'s aggregate bit was
+ * already taken (ty_is_agg); decode base's id once for both its aggregate bit
+ * and its unalias terminal. */
+ int scalar_local_place =
+ !is_bitfield && base.source_local != KIT_CG_LOCAL_NONE &&
+ base.op.kind == OPK_LOCAL &&
+ !api_sv_local_storage_is_aggregate(g, &base) && !ty_is_agg;
if (scalar_local_place) {
KitCgTypeId base_ty = api_sv_type(&base);
u8 base_bits = api_type_pred_bits(g->c, base_ty);
- scalar_local_place = !(base_bits & API_PRED_AGGREGATE) &&
- api_unalias_type(g->c, base_ty) ==
- api_unalias_type(g->c, ty);
+ scalar_local_place =
+ !(base_bits & API_PRED_AGGREGATE) &&
+ api_unalias_type(g->c, base_ty) == api_unalias_type(g->c, ty);
}
/* A still-delayed arith/cmp value going into a scalar local: emit the op
@@ -503,13 +610,25 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
* api_materialize_*_to with dst=base.op emits the same op the old temp got,
* just landing in base's storage; binops read both sources before writing the
* dst, so a self-referential RHS like `b = b + c` stays correct.) */
- if (scalar_local_place && (rv.kind == SV_ARITH || rv.kind == SV_CMP)) {
+ if (scalar_local_place &&
+ (api_sv_kind(&rv) == SV_ARITH || api_sv_kind(&rv) == SV_CMP)) {
Operand dst = base.op;
- if (rv.kind == SV_ARITH)
+ if (api_sv_kind(&rv) == SV_ARITH)
api_materialize_arith_to(g, &rv, dst);
else
api_materialize_cmp_to(g, &rv, dst);
api_local_const_clear(api_local_from_handle(g, base.source_local));
+ if (keep_result) {
+ ApiSValue out = api_make_sv(dst, ty);
+ out.lang_type = rv.lang_type;
+ out.lang_flags = rv.lang_flags;
+ api_sv_set_res(&out, RES_FIXED_LOCAL);
+ api_release(g, &base);
+ api_release(g, &rv);
+ api_push(g, out);
+ api_const_copy_top_from(g, rv_const);
+ return;
+ }
api_release(g, &base);
api_release(g, &rv);
return;
@@ -537,7 +656,7 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
/* `x = <value in a dead transient>`: flag the source dead so the -O0
* backend renames its register to x instead of emitting a routing mov
* (rv, its last reference, was popped above). */
- if (api_coalesce_on(g) && src.kind == OPK_LOCAL &&
+ if (!keep_result && api_coalesce_on(g) && src.kind == OPK_LOCAL &&
api_temp_dead(g, src.v.local))
src.flags |= OPK_FLAG_KILL;
T->copy(T, dst, src);
@@ -545,6 +664,10 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
if (base.source_local != KIT_CG_LOCAL_NONE)
api_local_const_clear(api_local_from_handle(g, base.source_local));
}
+ if (keep_result) {
+ src.flags &= (uint8_t)~OPK_FLAG_KILL;
+ api_push_kept_store_value(g, &rv, src, ty, rv_const);
+ }
api_release(g, &base);
api_release(g, &rv);
return;
@@ -572,12 +695,12 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
api_local_const_memory_boundary(g);
}
- /* Flag a dead-transient store value so the -O0 backend drops it after the store
- * instead of spilling it at the next barrier (eager dead-operand drop). mem_op
- * is the memory place (OPK_INDIRECT/GLOBAL, never a scalar local — that is the
- * scalar_local_place fast path above), so the dst self-guard is a no-op and the
- * store has already read the address before the drop. */
- src = api_op_kill_if_dead(g, src, mem_op);
+ /* Flag a dead-transient store value so the -O0 backend drops it after the
+ * store instead of spilling it at the next barrier (eager dead-operand drop).
+ * mem_op is the memory place (OPK_INDIRECT/GLOBAL, never a scalar local —
+ * that is the scalar_local_place fast path above), so the dst self-guard is a
+ * no-op and the store has already read the address before the drop. */
+ if (!keep_result) src = api_op_kill_if_dead(g, src, mem_op);
if (is_bitfield) {
/* A bit-field store rides the generic `store` with a bit-field MemAccess;
* the CgTarget impl does the read-modify-write insert. */
@@ -587,10 +710,22 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
T->store(T, mem_op, src, api_mem_from_access(g, &mem_op, access));
}
+ if (keep_result) {
+ src.flags &= (uint8_t)~OPK_FLAG_KILL;
+ api_push_kept_store_value(g, &rv, src, ty, rv_const);
+ }
api_release(g, &base);
api_release(g, &rv);
}
+void kit_cg_store(KitCg* g, KitCgMemAccess access) {
+ api_cg_store_impl(g, access, 0);
+}
+
+void kit_cg_store_keep(KitCg* g, KitCgMemAccess access) {
+ api_cg_store_impl(g, access, 1);
+}
+
/* ============================================================
* Stack manipulation
* ============================================================ */
@@ -598,15 +733,22 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
void kit_cg_dup(KitCg* g) {
ApiSValue v, dup;
ApiSValue* top;
+ ApiConstValue cv;
KitCgTypeId ty;
CGLocal r;
Operand dst;
if (!g || g->sp == 0) return;
top = &g->stack[g->sp - 1];
+ cv = api_const_at(g, 0);
+ if (api_unevaluated(g)) {
+ api_push(g, *top);
+ api_const_copy_top_from(g, cv);
+ return;
+ }
api_ensure_local(g, top);
v = *top;
- if (v.res != RES_LOCAL) {
- if (v.res == RES_FIXED_LOCAL && !api_is_lvalue_sv(&v) &&
+ if (api_sv_res(&v) != RES_LOCAL) {
+ if (api_sv_res(&v) == RES_FIXED_LOCAL && !api_is_lvalue_sv(&v) &&
v.op.kind == OPK_LOCAL) {
ty = api_owned_local_type(g, &v);
r = api_alloc_temp_local(g, ty);
@@ -615,41 +757,46 @@ void kit_cg_dup(KitCg* g) {
api_op_local((CGLocal)api_local_of_sv(&v), ty));
dup = v;
api_set_owned_local(&dup, r);
- dup.res = RES_LOCAL;
- dup.pinned = 0;
+ api_sv_set_res(&dup, RES_LOCAL);
+ api_sv_set_pinned(&dup, 0);
dup.source_local = KIT_CG_LOCAL_NONE;
g->stack[g->sp - 1] = dup;
api_push(g, v);
+ api_const_copy_top_from(g, cv);
return;
}
api_push(g, v);
+ api_const_copy_top_from(g, cv);
return;
}
if (api_coalesce_on(g)) {
/* Lazy dup: push a second reference to the owned temp instead of copying it
* into a fresh one. Both entries only ever read the write-once value temp,
- * so they can share it; the value-stack refcount (now 2) keeps finer-reclaim
- * and copy-adoption from recycling the temp while it is still shared, and it
- * is freed once both references are gone (Fix A at the statement boundary, or
- * finer reclaim when the count returns to 0). Eliminates the common
- * assignment-result dup whose value is stored once and then discarded. */
+ * so they can share it; the value-stack refcount (now 2) keeps
+ * finer-reclaim and copy-adoption from recycling the temp while it is still
+ * shared, and it is freed once both references are gone (Fix A at the
+ * statement boundary, or finer reclaim when the count returns to 0).
+ * Eliminates the common assignment-result dup whose value is stored once
+ * and then discarded. */
dup = v;
- dup.pinned = 0;
+ api_sv_set_pinned(&dup, 0);
api_push(g, dup);
+ api_const_copy_top_from(g, cv);
return;
}
- top->pinned = 1;
+ api_sv_set_pinned(top, 1);
ty = api_owned_local_type(g, &v);
r = api_alloc_temp_local(g, ty);
dst = api_op_local(r, ty);
g->target->copy(g->target, dst,
api_op_local((CGLocal)api_local_of_sv(&v), ty));
- g->stack[g->sp - 1].pinned = 0;
+ api_sv_set_pinned(&g->stack[g->sp - 1], 0);
dup = v;
api_set_owned_local(&dup, r);
- dup.res = RES_LOCAL;
- dup.pinned = 0;
+ api_sv_set_res(&dup, RES_LOCAL);
+ api_sv_set_pinned(&dup, 0);
api_push(g, dup);
+ api_const_copy_top_from(g, cv);
}
/* Duplicate the top two value-stack entries. The lower of the two is the deeper
@@ -685,10 +832,14 @@ void kit_cg_dup2(KitCg* g) {
void kit_cg_swap(KitCg* g) {
ApiSValue tmp;
+ ApiConstValue ctmp;
if (!g || g->sp < 2) return;
tmp = g->stack[g->sp - 1];
g->stack[g->sp - 1] = g->stack[g->sp - 2];
g->stack[g->sp - 2] = tmp;
+ ctmp = g->const_stack[g->sp - 1];
+ g->const_stack[g->sp - 1] = g->const_stack[g->sp - 2];
+ g->const_stack[g->sp - 2] = ctmp;
}
void kit_cg_drop(KitCg* g) {
@@ -698,30 +849,57 @@ void kit_cg_drop(KitCg* g) {
api_release(g, &v);
}
-uint32_t kit_cg_stack_depth(KitCg* g) { return g ? g->sp : 0u; }
+KitCgSlotInfo kit_cg_slot_info(KitCg* g, uint32_t depth_from_top) {
+ KitCgSlotInfo info;
+ memset(&info, 0, sizeof info);
+ if (!g || depth_from_top >= g->sp) return info;
+ {
+ ApiSValue* sv = &g->stack[g->sp - 1u - depth_from_top];
+ info.cg_type = api_sv_type(sv);
+ info.lang_type = sv->lang_type;
+ info.lang_flags = sv->lang_flags;
+ }
+ return info;
+}
-int kit_cg_top_const_int(KitCg* g, int64_t* out_value) {
- ApiSValue* v;
- KitCgTypeId ty;
- u32 width;
- if (!g || !out_value || !g->sp) return 0;
- v = &g->stack[g->sp - 1u];
- if (v->kind != SV_OPERAND || v->op.kind != OPK_IMM) return 0;
- ty = api_sv_type(v);
- if (!api_foldable_int_like_type(g->c, ty, &width)) return 0;
- *out_value = api_fold_result(g->c, ty, (u64)v->op.v.imm, width);
- return 1;
+void kit_cg_retag_at(KitCg* g, uint32_t depth_from_top, const void* lang_type,
+ uint16_t lang_flags) {
+ ApiSValue* sv;
+ if (!g || depth_from_top >= g->sp) return;
+ sv = &g->stack[g->sp - 1u - depth_from_top];
+ sv->lang_type = lang_type;
+ sv->lang_flags = lang_flags;
+}
+
+void kit_cg_retag_top(KitCg* g, const void* lang_type, uint16_t lang_flags) {
+ kit_cg_retag_at(g, 0, lang_type, lang_flags);
}
+void kit_cg_set_top_flags(KitCg* g, uint16_t set, uint16_t clear) {
+ ApiSValue* sv;
+ if (!g || !g->sp) return;
+ sv = &g->stack[g->sp - 1u];
+ sv->lang_flags = (uint16_t)((sv->lang_flags | set) & (uint16_t)~clear);
+}
+
+uint32_t kit_cg_stack_depth(KitCg* g) { return g ? g->sp : 0u; }
+
void kit_cg_rot3(KitCg* g) {
ApiSValue a, b, c;
+ ApiConstValue ca, cb, cc;
if (!g || g->sp < 3) return;
a = g->stack[g->sp - 3];
b = g->stack[g->sp - 2];
c = g->stack[g->sp - 1];
+ ca = g->const_stack[g->sp - 3];
+ cb = g->const_stack[g->sp - 2];
+ cc = g->const_stack[g->sp - 1];
g->stack[g->sp - 3] = b;
g->stack[g->sp - 2] = c;
g->stack[g->sp - 1] = a;
+ g->const_stack[g->sp - 3] = cb;
+ g->const_stack[g->sp - 2] = cc;
+ g->const_stack[g->sp - 1] = ca;
}
/* ============================================================
diff --git a/src/cg/session.c b/src/cg/session.c
@@ -17,6 +17,10 @@ static void cg_free_obj_state(KitCg* g) {
h->free(h, g->stack, sizeof(ApiSValue) * g->cap);
g->stack = NULL;
}
+ if (g->const_stack) {
+ h->free(h, g->const_stack, sizeof(ApiConstValue) * g->cap);
+ g->const_stack = NULL;
+ }
if (g->locals) {
h->free(h, g->locals, sizeof(*g->locals) * g->locals_cap);
g->locals = NULL;
@@ -54,6 +58,7 @@ static void cg_free_obj_state(KitCg* g) {
g->delayed_free = NULL;
g->sp = 0;
g->cap = 0;
+ g->unevaluated_depth = 0;
g->nlocals = 0;
g->const_head = KIT_CG_LOCAL_NONE;
g->locals_cap = 0;
@@ -496,8 +501,9 @@ void kit_cg_func_end(KitCg* g) {
void kit_cg_reclaim_temps(KitCg* g) {
if (!g) return;
/* Only safe when the value stack is empty: every transient temp is then
- * provably dead. The guard also makes this a no-op inside any future construct
- * (e.g. a statement-expression) that leaves a value live across a boundary. */
+ * provably dead. The guard also makes this a no-op inside any future
+ * construct (e.g. a statement-expression) that leaves a value live across a
+ * boundary. */
if (g->sp != 0) return;
if (g->target && g->target->reclaim_temps)
g->target->reclaim_temps(g->target);
diff --git a/src/cg/type.c b/src/cg/type.c
@@ -23,7 +23,7 @@ typedef struct CgApiType {
const KitCgField* fields;
const KitCgEnumValue* values;
const KitCgFuncParam* params;
- KitCgFuncResult result; /* result.type == KIT_CG_TYPE_NONE == void */
+ KitCgFuncResult result; /* void builtin means no value */
KitCgCallConv call_conv;
u8 kind;
u8 abi_variadic;
@@ -58,34 +58,34 @@ typedef struct CgApiType {
* cg/type.h so multi-predicate-on-same-id call sites can decode an id once. */
/* High bit of CgApiType.cached_class: set once the class has been computed, so
- * a genuine {WK_NARROW, non-aggregate} (packed 0) is told apart from unfilled. */
+ * a genuine {WK_NARROW, non-aggregate} (packed 0) is told apart from unfilled.
+ */
#define API_TYPE_CLASS_CACHED 0x80u
-SEGVEC_DEFINE(CgApiTypes, CgApiType, CG_API_TYPE_SEG_SHIFT);
+enum { CG_API_TYPES_SEG_SHIFT = 6 };
+
+SEGVEC_DEFINE(CgApiTypes, CgApiType, CG_API_TYPES_SEG_SHIFT);
/* Structural dedup of derived ptr/array types, O(1) instead of a linear scan of
* the whole type table on every derivation (which made each derived-type use
* O(#types) — the dominant frontend hotspot on type-heavy input like sqlite).
- * The key packs the structural identity into a u64: ptr = (pointee<<32)|addr_sp,
- * array = (elem<<32)|count. Both component high halves (pointee/elem) are real
- * type ids, never KIT_CG_TYPE_NONE (0), so the packed key is always nonzero and
- * 0 is a safe empty-slot sentinel. Value is the interned KitCgTypeId. */
+ * Pointer keys fit in a u64: ptr = (pointee<<32)|addr_sp. Arrays use the
+ * structural hashset below so their full uint64_t count participates in
+ * identity. */
static inline u64 cg_ptr_key(KitCgTypeId pointee, u32 address_space) {
return ((u64)pointee << 32) | (u64)address_space;
}
-static inline u64 cg_array_key(KitCgTypeId elem, u64 count) {
- return ((u64)elem << 32) | (count & 0xffffffffu);
-}
KIT_HASHMAP_DEFINE(CgPtrMap, u64, KitCgTypeId, hash_u64);
-KIT_HASHMAP_DEFINE(CgArrayMap, u64, KitCgTypeId, hash_u64);
-/* Function types have a structural key (result + params + variadic + conv) that
- * a scalar hashmap can't express, so dedup them through a structural hashset of
- * the stored CgApiType* (SEGVEC entries have stable addresses). The callbacks
- * are defined further down (after the shared result/param comparators); forward
- * declarations let the set be defined here, before CgApiState embeds it. */
+/* Array and function types have structural keys that a scalar hashmap cannot
+ * express (array counts are full uint64_t; function keys include attrs), so
+ * dedup them through structural hashsets of the stored CgApiType* (SEGVEC
+ * entries have stable addresses). The callbacks are defined further down. */
+static u32 cg_array_hash(CgApiType* const e);
+static int cg_array_eq(CgApiType* const a, CgApiType* const b);
static u32 cg_func_hash(CgApiType* const e);
static int cg_func_eq(CgApiType* const a, CgApiType* const b);
+KIT_HASHSET_DEFINE(CgArraySet, CgApiType*, cg_array_hash, cg_array_eq);
KIT_HASHSET_DEFINE(CgFuncSet, CgApiType*, cg_func_hash, cg_func_eq);
typedef struct CgApiState {
@@ -93,7 +93,7 @@ typedef struct CgApiState {
CgApiTypes types;
/* Structural dedup indexes for derived ptr/array/func types (see above). */
CgPtrMap ptr_index;
- CgArrayMap array_index;
+ CgArraySet array_index;
CgFuncSet func_index;
CgType builtins[KIT_CG_BUILTIN_COUNT];
/* Packed API_TYPE_CLASS_* per builtin, precomputed at init so the hottest
@@ -108,32 +108,22 @@ typedef struct CgApiState {
u8 pad[3];
} CgApiState;
-static KitCgTypeId type_id_from_tuple(u32 seg, u32 index) {
- return (KitCgTypeId)((seg << CG_API_TYPE_SEG_SHIFT) |
- (index & CG_API_TYPE_SEG_MASK));
-}
-
KitCgTypeId builtin_id(KitCgBuiltinType t) {
- return type_id_from_tuple(CG_API_TYPE_BUILTIN_SEG, (u32)t);
+ if ((u32)t >= KIT_CG_BUILTIN_COUNT) return KIT_CG_TYPE_NONE;
+ return (KitCgTypeId)((u32)t + 1u);
}
static int decode_user_id(KitCgTypeId id, u32* index_out) {
- u32 seg = id >> CG_API_TYPE_SEG_SHIFT;
- u32 off = id & CG_API_TYPE_SEG_MASK;
- if (seg < CG_API_TYPE_USER_SEG_BIAS) return 0;
- *index_out =
- ((seg - CG_API_TYPE_USER_SEG_BIAS) << CG_API_TYPE_SEG_SHIFT) | off;
+ if (!index_out) return 0;
+ if (id <= KIT_CG_BUILTIN_COUNT) return 0;
+ *index_out = id - (KitCgTypeId)KIT_CG_BUILTIN_COUNT - 1u;
return 1;
}
static KitCgTypeId user_id_from_index(u32 index) {
- u32 raw_seg = index >> CG_API_TYPE_SEG_SHIFT;
- u32 off = index & CG_API_TYPE_SEG_MASK;
- u32 seg_limit = UINT32_MAX >> CG_API_TYPE_SEG_SHIFT;
- if (raw_seg > seg_limit - CG_API_TYPE_USER_SEG_BIAS) {
+ if (index > UINT32_MAX - (u32)KIT_CG_BUILTIN_COUNT - 1u)
return KIT_CG_TYPE_NONE;
- }
- return type_id_from_tuple(raw_seg + CG_API_TYPE_USER_SEG_BIAS, off);
+ return (KitCgTypeId)((u32)KIT_CG_BUILTIN_COUNT + 1u + index);
}
static u64 cg_align_to(u64 n, u32 align) {
@@ -307,7 +297,8 @@ static void cg_api_init_builtins(Compiler* c, CgApiState* s) {
* both live (builtin_cg_type_init already consults c->abi for the va_list
* builtin, so it is ready). */
for (u32 i = 0; i < KIT_CG_BUILTIN_COUNT; ++i) {
- s->builtin_class[i] = api_compute_type_class(c, builtin_id((KitCgBuiltinType)i));
+ s->builtin_class[i] =
+ api_compute_type_class(c, builtin_id((KitCgBuiltinType)i));
/* Builtins are never aliases, so the kind-derived predicate bitset is the
* exact, final value — classify the node once here, then every predicate
* query is one indexed load (see api_type_pred). */
@@ -319,13 +310,11 @@ static void cg_api_init_builtins(Compiler* c, CgApiState* s) {
u8 api_type_class(Compiler* c, KitCgTypeId ty) {
CgApiState* s;
CgApiType* e;
- u32 seg;
if (ty == KIT_CG_TYPE_NONE) return 0;
s = c->cg_api ? (CgApiState*)c->cg_api : cg_api_get(c);
if (!s) return api_compute_type_class(c, ty);
- seg = ty >> CG_API_TYPE_SEG_SHIFT;
- if (seg == CG_API_TYPE_BUILTIN_SEG) {
- u32 off = ty & CG_API_TYPE_SEG_MASK;
+ if (ty <= KIT_CG_BUILTIN_COUNT) {
+ u32 off = ty - 1u;
if (off < KIT_CG_BUILTIN_COUNT) return s->builtin_class[off];
return api_compute_type_class(c, ty);
}
@@ -340,7 +329,7 @@ int api_type_layout_get(Compiler* c, KitCgTypeId ty, u32* size, u32* align,
u8* scalar_kind, u8* signed_, u8* atomic) {
CgApiType* e;
if (ty == KIT_CG_TYPE_NONE) return 0;
- if ((ty >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) return 0;
+ if (ty <= KIT_CG_BUILTIN_COUNT) return 0;
e = api_type_from_id(c, ty);
if (!e || !e->abi_cached) return 0;
*size = e->abi_size;
@@ -355,7 +344,8 @@ void api_type_layout_put(Compiler* c, KitCgTypeId ty, u32 size, u32 align,
u8 scalar_kind, u8 signed_, u8 atomic) {
CgApiType* e;
if (ty == KIT_CG_TYPE_NONE) return;
- if ((ty >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) return;
+ if (ty <= KIT_CG_BUILTIN_COUNT) return;
+ if (!align && scalar_kind == ABI_SC_VOID) return;
e = api_type_from_id(c, ty);
if (!e) return;
e->abi_size = size;
@@ -378,7 +368,7 @@ static CgApiState* cg_api_get(Compiler* c) {
s->heap = h;
CgApiTypes_init(&s->types, h);
CgPtrMap_init(&s->ptr_index, h);
- CgArrayMap_init(&s->array_index, h);
+ CgArraySet_init(&s->array_index, h);
CgFuncSet_init(&s->func_index, h);
c->cg_api = s;
c->cg_api_free = cg_api_fini;
@@ -387,15 +377,12 @@ static CgApiState* cg_api_get(Compiler* c) {
}
const CgType* cg_type_get(Compiler* c, KitCgTypeId id) {
- u32 seg;
u32 off;
CgApiState* s;
CgApiType* e;
if (!c || id == KIT_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 >= KIT_CG_BUILTIN_COUNT) return NULL;
+ if (id <= KIT_CG_BUILTIN_COUNT) {
+ off = id - 1u;
/* Fast path: the per-compiler state (with its builtin table) is created
* on first use and then stays put, so inline the already-initialized case
* -- the hot per-op type query becomes a load + index instead of a call
@@ -409,13 +396,11 @@ const CgType* cg_type_get(Compiler* c, KitCgTypeId id) {
}
uint64_t cg_type_size(Compiler* c, KitCgTypeId id) {
- const CgType* ty = cg_type_get(c, id);
- return ty ? ty->size : 0;
+ return c && c->abi ? abi_cg_sizeof(c->abi, id) : 0;
}
uint32_t cg_type_align(Compiler* c, KitCgTypeId id) {
- const CgType* ty = cg_type_get(c, id);
- return ty ? ty->align : 0;
+ return c && c->abi ? abi_cg_alignof(c->abi, id) : 0;
}
/* Predicate bitset of a single (already-unaliased) CgType node. The kind->bit
@@ -442,15 +427,16 @@ static u8 api_pred_bits_for_kind(const CgType* ty) {
/* Full API_PRED_* bitset for `id` via exactly one decode: builtin fast path (no
* aliasing, no entry) or a descriptor load off the user-type entry, filled
- * lazily on first miss. Multi-predicate-on-the-same-id call sites read this once
- * and then test the masks locally instead of decoding `id` per predicate. */
+ * lazily on first miss. Multi-predicate-on-the-same-id call sites read this
+ * once and then test the masks locally instead of decoding `id` per predicate.
+ */
u8 api_type_pred_bits(Compiler* c, KitCgTypeId id) {
CgApiType* e;
if (id == KIT_CG_TYPE_NONE) return 0;
- if ((id >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) {
+ if (id <= KIT_CG_BUILTIN_COUNT) {
/* Builtins are never aliases; their predicate bitset is precomputed at
* init, so this is one indexed load (no cg_type_get + re-classify). */
- u32 off = id & CG_API_TYPE_SEG_MASK;
+ u32 off = id - 1u;
CgApiState* s = c->cg_api ? (CgApiState*)c->cg_api : cg_api_get(c);
if (s && off < KIT_CG_BUILTIN_COUNT) return s->builtin_pred[off];
return api_pred_bits_for_kind(cg_type_get(c, id));
@@ -467,8 +453,8 @@ u8 api_type_pred_bits(Compiler* c, KitCgTypeId id) {
return e->pred_bits;
}
-/* One predicate query for `id`: returns (bits & mask) != 0 off the single-decode
- * bitset. Mirrors api_type_class's CACHED-bit memo. */
+/* One predicate query for `id`: returns (bits & mask) != 0 off the
+ * single-decode bitset. Mirrors api_type_class's CACHED-bit memo. */
static int api_type_pred(Compiler* c, KitCgTypeId id, u8 mask) {
return (api_type_pred_bits(c, id) & mask) != 0;
}
@@ -513,13 +499,11 @@ KitCgTypeId cg_type_func_ret_id(Compiler* c, KitCgTypeId id) {
if (ty && ty->kind == KIT_CG_TYPE_ALIAS)
return cg_type_func_ret_id(c, ty->alias.base);
if (!ty || ty->kind != KIT_CG_TYPE_FUNC) return KIT_CG_TYPE_NONE;
- return ty->func.result.type ? ty->func.result.type
- : builtin_id(KIT_CG_BUILTIN_VOID);
+ return ty->func.result.type;
}
KitCgTypeId cg_func_ret_type(const CgType* fnty) {
- return fnty->func.result.type ? fnty->func.result.type
- : builtin_id(KIT_CG_BUILTIN_VOID);
+ return fnty->func.result.type;
}
KitCgTypeId cg_type_func_result_id(Compiler* c, KitCgTypeId id) {
@@ -565,11 +549,16 @@ static KitCgTypeId find_ptr_type_id(Compiler* c, KitCgTypeId pointee,
static KitCgTypeId find_array_type_id(Compiler* c, KitCgTypeId elem,
u64 count) {
CgApiState* s;
- const KitCgTypeId* hit;
+ CgApiType probe;
+ CgApiType* hit;
if (!c || !c->cg_api) return KIT_CG_TYPE_NONE;
s = (CgApiState*)c->cg_api;
- hit = CgArrayMap_get(&s->array_index, cg_array_key(elem, count));
- return hit ? *hit : KIT_CG_TYPE_NONE;
+ memset(&probe, 0, sizeof probe);
+ probe.kind = CG_API_TYPE_ARRAY;
+ probe.base = elem;
+ probe.array_count = count;
+ hit = CgArraySet_find(&s->array_index, &probe);
+ return hit ? hit->self_id : KIT_CG_TYPE_NONE;
}
static int cg_params_eq(const KitCgFuncParam* a, const KitCgFuncParam* b,
@@ -587,6 +576,16 @@ static int cg_result_eq(const KitCgFuncResult* a, const KitCgFuncResult* b) {
memcmp(&a->attrs, &b->attrs, sizeof(a->attrs)) == 0;
}
+static u32 cg_array_hash(CgApiType* const e) {
+ u32 h = hash_u32((u32)e->base);
+ h ^= hash_u64(e->array_count) + 0x9e3779b9u + (h << 6) + (h >> 2);
+ return h;
+}
+
+static int cg_array_eq(CgApiType* const a, CgApiType* const b) {
+ return a->base == b->base && a->array_count == b->array_count;
+}
+
/* Structural hash/eq over a function type's identity — the same fields the old
* linear scan compared. Only CG_API_TYPE_FUNC entries enter the set, so neither
* callback re-checks kind. The result/param attrs are compared (not hashed):
@@ -633,7 +632,6 @@ static CgApiType* api_type_from_id(Compiler* c, KitCgTypeId id) {
CgApiState* s;
CgApiType* e;
if (!c || id == KIT_CG_TYPE_NONE) return NULL;
- if ((id >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) return NULL;
if (!decode_user_id(id, &index)) return NULL;
s = (CgApiState*)c->cg_api;
if (!s) return NULL;
@@ -650,8 +648,7 @@ KitCgTypeId api_unalias_type(Compiler* c, KitCgTypeId id) {
const CgType* ty;
KitCgTypeId start = id;
/* Builtins are never aliases — no entry, no chain to walk or cache. */
- if (id == KIT_CG_TYPE_NONE ||
- (id >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) {
+ if (id == KIT_CG_TYPE_NONE || id <= KIT_CG_BUILTIN_COUNT) {
ty = cg_type_get(c, id);
return ty ? id : KIT_CG_TYPE_NONE;
}
@@ -703,6 +700,80 @@ static CgTypeField* copy_cg_fields(Compiler* c, const KitCgField* src, u32 n) {
return dst;
}
+static int cg_type_record_complete(const CgType* ty) {
+ return ty && ty->kind == KIT_CG_TYPE_RECORD &&
+ (ty->record.flags & CG_TYPE_RECORD_COMPLETE) != 0;
+}
+
+static int cg_type_complete_id(Compiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ if (!ty) return 0;
+ switch (ty->kind) {
+ case KIT_CG_TYPE_ALIAS:
+ return cg_type_complete_id(c, ty->alias.base);
+ case KIT_CG_TYPE_ARRAY:
+ return cg_type_complete_id(c, ty->array.elem);
+ case KIT_CG_TYPE_RECORD:
+ return cg_type_record_complete(ty);
+ default:
+ return 1;
+ }
+}
+
+static int cg_type_sized_id(Compiler* c, KitCgTypeId id) {
+ ABITypeInfo ti;
+ const CgType* ty;
+ id = api_unalias_type(c, id);
+ ty = cg_type_get(c, id);
+ if (!ty) return 0;
+ switch (ty->kind) {
+ case KIT_CG_TYPE_VOID:
+ return 0;
+ case KIT_CG_TYPE_FUNC:
+ return 0;
+ case KIT_CG_TYPE_RECORD:
+ if (!cg_type_record_complete(ty)) return 0;
+ break;
+ case KIT_CG_TYPE_ARRAY:
+ if (!cg_type_sized_id(c, ty->array.elem)) return 0;
+ break;
+ default:
+ break;
+ }
+ ti = abi_cg_type_info(c->abi, id);
+ return ti.align != 0;
+}
+
+static int cg_type_valid_by_value(Compiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, api_unalias_type(c, id));
+ if (!ty) return 0;
+ if (ty->kind == KIT_CG_TYPE_VOID || ty->kind == KIT_CG_TYPE_FUNC) return 0;
+ return cg_type_sized_id(c, id);
+}
+
+static int cg_type_contains_by_value(Compiler* c, KitCgTypeId id,
+ KitCgTypeId needle) {
+ const CgType* ty;
+ id = api_unalias_type(c, id);
+ if (!id) return 0;
+ if (id == needle) return 1;
+ ty = cg_type_get(c, id);
+ if (!ty) return 0;
+ switch (ty->kind) {
+ case KIT_CG_TYPE_ARRAY:
+ return cg_type_contains_by_value(c, ty->array.elem, needle);
+ case KIT_CG_TYPE_RECORD:
+ if (!cg_type_record_complete(ty)) return 0;
+ for (u32 i = 0; i < ty->record.nfields; ++i) {
+ if (cg_type_contains_by_value(c, ty->record.fields[i].type, needle))
+ return 1;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+}
+
static int cg_type_layout_record(Compiler* c, CgType* cg) {
u32 max_align = 1;
u64 size = 0;
@@ -812,12 +883,14 @@ static int cg_type_set_ptr(Compiler* c, CgApiType* e, KitCgTypeId pointee,
static int cg_type_set_array(Compiler* c, CgApiType* e, KitCgTypeId elem,
u64 count) {
- const CgType* ety = cg_type_get(c, elem);
- if (!ety) return 0;
+ ABITypeInfo ety;
+ if (!cg_type_sized_id(c, elem)) return 0;
+ ety = abi_cg_type_info(c->abi, elem);
+ if (ety.size && count > UINT32_MAX / ety.size) return 0;
memset(&e->cg, 0, sizeof(e->cg));
e->cg.kind = KIT_CG_TYPE_ARRAY;
- e->cg.size = ety->size * count;
- e->cg.align = ety->align;
+ e->cg.size = (u64)ety.size * count;
+ e->cg.align = ety.align;
e->cg.array.elem = elem;
e->cg.array.count = count;
return 1;
@@ -826,11 +899,13 @@ static int cg_type_set_array(Compiler* c, CgApiType* e, KitCgTypeId elem,
static int cg_type_set_alias(Compiler* c, CgApiType* e, KitSym name,
KitCgTypeId base) {
const CgType* bty = cg_type_get(c, base);
+ ABITypeInfo ti;
if (!bty) return 0;
+ ti = abi_cg_type_info(c->abi, base);
memset(&e->cg, 0, sizeof(e->cg));
e->cg.kind = KIT_CG_TYPE_ALIAS;
- e->cg.size = bty->size;
- e->cg.align = bty->align;
+ e->cg.size = ti.size;
+ e->cg.align = ti.align;
e->cg.alias.name = name;
e->cg.alias.base = base;
return 1;
@@ -856,16 +931,18 @@ static int cg_type_set_enum(Compiler* c, CgApiType* e, KitSym tag,
KitCgTypeId base, KitCgEnumValue* values,
u32 nvalues) {
const CgType* bty;
+ ABITypeInfo ti;
if (base == KIT_CG_TYPE_NONE) base = builtin_id(KIT_CG_BUILTIN_I32);
bty = cg_type_get(c, base);
if (!bty ||
!(bty->kind == KIT_CG_TYPE_INT || bty->kind == KIT_CG_TYPE_BOOL)) {
return 0;
}
+ ti = abi_cg_type_info(c->abi, base);
memset(&e->cg, 0, sizeof(e->cg));
e->cg.kind = KIT_CG_TYPE_ENUM;
- e->cg.size = bty->size;
- e->cg.align = bty->align;
+ e->cg.size = ti.size;
+ e->cg.align = ti.align;
e->cg.enum_.tag = tag;
e->cg.enum_.base = base;
e->cg.enum_.values = values;
@@ -875,9 +952,12 @@ static int cg_type_set_enum(Compiler* c, CgApiType* e, KitSym tag,
static int cg_type_set_func(Compiler* c, CgApiType* e, KitCgFuncSig sig,
KitCgFuncParam* params) {
- if (sig.result.type && !cg_type_get(c, sig.result.type)) return 0;
+ KitCgTypeId void_ty = builtin_id(KIT_CG_BUILTIN_VOID);
+ if (!sig.result.type || !cg_type_get(c, sig.result.type)) return 0;
+ if (sig.result.type != void_ty && !cg_type_valid_by_value(c, sig.result.type))
+ return 0;
for (u32 i = 0; i < sig.nparams; ++i) {
- if (!cg_type_get(c, sig.params[i].type)) return 0;
+ if (!cg_type_valid_by_value(c, sig.params[i].type)) return 0;
}
memset(&e->cg, 0, sizeof(e->cg));
e->cg.kind = KIT_CG_TYPE_FUNC;
@@ -891,17 +971,7 @@ static int cg_type_set_func(Compiler* c, CgApiType* e, KitCgFuncSig sig,
return 1;
}
-KitCgBuiltinTypes kit_cg_builtin_types(KitCompiler* c) {
- KitCgBuiltinTypes out;
- (void)c;
- memset(&out, 0, sizeof(out));
- for (u32 i = 0; i < KIT_CG_BUILTIN_COUNT; ++i) {
- out.id[i] = builtin_id((KitCgBuiltinType)i);
- }
- return out;
-}
-
-KitCgTypeId kit_cg_builtin_type_id(KitCompiler* c, KitCgBuiltinType which) {
+KitCgTypeId kit_cg_type_builtin(KitCompiler* c, KitCgBuiltinType which) {
(void)c;
if ((u32)which >= KIT_CG_BUILTIN_COUNT) return KIT_CG_TYPE_NONE;
return builtin_id(which);
@@ -931,7 +1001,7 @@ KitCgTypeId kit_cg_type_array(KitCompiler* c, KitCgTypeId elem,
uint64_t count) {
KitCgTypeId id;
CgApiType* e;
- if (!cg_type_get(c, elem) || count > UINT32_MAX) return KIT_CG_TYPE_NONE;
+ if (!cg_type_sized_id(c, elem)) return KIT_CG_TYPE_NONE;
id = find_array_type_id(c, elem, count);
if (id != KIT_CG_TYPE_NONE) return id;
e = type_alloc(c, &id);
@@ -942,8 +1012,7 @@ KitCgTypeId kit_cg_type_array(KitCompiler* c, KitCgTypeId elem,
if (!cg_type_set_array(c, e, elem, count)) {
return KIT_CG_TYPE_NONE;
}
- CgArrayMap_set(&((CgApiState*)c->cg_api)->array_index,
- cg_array_key(elem, count), id);
+ CgArraySet_add(&((CgApiState*)c->cg_api)->array_index, e);
return id;
}
@@ -959,44 +1028,56 @@ KitCgTypeId kit_cg_type_alias(KitCompiler* c, KitSym name, KitCgTypeId base) {
return cg_type_set_alias(c, e, name, base) ? id : KIT_CG_TYPE_NONE;
}
-KitCgTypeId kit_cg_type_record(KitCompiler* c, KitSym tag,
- const KitCgField* fields, uint32_t nfields) {
- KitCgRecordDesc desc;
- memset(&desc, 0, sizeof desc);
- desc.tag = tag;
- desc.fields = fields;
- desc.nfields = nfields;
- return kit_cg_type_record_ex(c, &desc);
+KitCgTypeId kit_cg_type_record_decl(KitCompiler* c, KitSym tag, int is_union) {
+ KitCgTypeId id;
+ CgApiType* e;
+ if (!c) return KIT_CG_TYPE_NONE;
+ e = type_alloc(c, &id);
+ if (!e) return KIT_CG_TYPE_NONE;
+ e->name = tag;
+ e->count = 0;
+ e->fields = NULL;
+ e->kind = CG_API_TYPE_RECORD;
+ memset(&e->cg, 0, sizeof(e->cg));
+ e->cg.kind = KIT_CG_TYPE_RECORD;
+ e->cg.record.tag = tag;
+ e->cg.record.is_union = is_union != 0;
+ return id;
}
-KitCgTypeId kit_cg_type_record_ex(KitCompiler* c, const KitCgRecordDesc* desc) {
- KitCgTypeId id;
+KitStatus kit_cg_type_record_complete(KitCompiler* c, KitCgTypeId record,
+ const KitCgRecordDesc* desc) {
CgApiType* e;
KitCgField* copied = NULL;
if (!c || !desc || (desc->nfields && !desc->fields) ||
desc->nfields > UINT16_MAX) {
- return KIT_CG_TYPE_NONE;
+ return KIT_INVALID;
}
+ e = api_type_from_id(c, record);
+ if (!e || e->cg.kind != KIT_CG_TYPE_RECORD) return KIT_INVALID;
+ if (e->cg.record.flags & CG_TYPE_RECORD_COMPLETE) return KIT_INVALID;
+ if (e->cg.record.is_union != (desc->is_union != 0)) return KIT_INVALID;
+
if (desc->nfields) {
copied = arena_array(&c->global->arena, KitCgField, desc->nfields);
- if (!copied) return KIT_CG_TYPE_NONE;
+ if (!copied) return KIT_NOMEM;
}
-
for (u32 i = 0; i < desc->nfields; ++i) {
- if (!cg_type_get(c, desc->fields[i].type)) return KIT_CG_TYPE_NONE;
+ KitCgTypeId fty = desc->fields[i].type;
+ if (!cg_type_valid_by_value(c, fty)) return KIT_INVALID;
+ if (cg_type_contains_by_value(c, fty, record)) return KIT_INVALID;
copied[i] = desc->fields[i];
}
- e = type_alloc(c, &id);
- if (!e) return KIT_CG_TYPE_NONE;
- e->name = desc->tag;
+
+ e->name = desc->tag ? desc->tag : e->name;
e->count = desc->nfields;
e->fields = copied;
- e->kind = CG_API_TYPE_RECORD;
- if (!cg_type_set_record(c, e, desc->tag, desc->fields, desc->nfields,
- desc->is_union, desc->align_override, 0)) {
- return KIT_CG_TYPE_NONE;
+ if (!cg_type_set_record(c, e, e->name, desc->fields, desc->nfields,
+ desc->is_union, desc->align_override,
+ CG_TYPE_RECORD_COMPLETE)) {
+ return KIT_INVALID;
}
- return id;
+ return KIT_OK;
}
KitCgTypeId kit_cg_type_enum(KitCompiler* c, KitSym tag, KitCgTypeId base,
@@ -1032,16 +1113,19 @@ KitCgTypeId kit_cg_type_func(KitCompiler* c, KitCgFuncSig sig) {
if (!c || (sig.nparams && !sig.params) || sig.nparams > UINT16_MAX) {
return KIT_CG_TYPE_NONE;
}
- if (sig.result.type && !cg_type_get(c, sig.result.type))
+ if (!sig.result.type || !cg_type_get(c, sig.result.type))
return KIT_CG_TYPE_NONE;
+ if (sig.result.type != builtin_id(KIT_CG_BUILTIN_VOID) &&
+ !cg_type_valid_by_value(c, sig.result.type))
+ return KIT_CG_TYPE_NONE;
+ for (u32 i = 0; i < sig.nparams; ++i) {
+ if (!cg_type_valid_by_value(c, sig.params[i].type)) return KIT_CG_TYPE_NONE;
+ }
id = find_func_type_id(c, sig);
if (id != KIT_CG_TYPE_NONE) return id;
if (sig.nparams) {
copied = copy_cg_params(c, sig.params, sig.nparams);
if (!copied) return KIT_CG_TYPE_NONE;
- for (u32 i = 0; i < sig.nparams; ++i) {
- if (!cg_type_get(c, sig.params[i].type)) return KIT_CG_TYPE_NONE;
- }
}
e = type_alloc(c, &id);
if (!e) return KIT_CG_TYPE_NONE;
@@ -1066,11 +1150,174 @@ uint32_t kit_cg_type_align(KitCompiler* c, KitCgTypeId id) {
return cg_type_align(c, id);
}
+KitCgTypeId kit_cg_type_resolve_alias(KitCompiler* c, KitCgTypeId id) {
+ return api_unalias_type(c, id);
+}
+
+int kit_cg_type_is_void(KitCompiler* c, KitCgTypeId id) {
+ KitCgTypeId u = api_unalias_type(c, id);
+ const CgType* ty = cg_type_get(c, u);
+ return ty && ty->kind == KIT_CG_TYPE_VOID;
+}
+
+int kit_cg_type_is_complete(KitCompiler* c, KitCgTypeId id) {
+ return cg_type_complete_id(c, id);
+}
+
+int kit_cg_type_is_sized(KitCompiler* c, KitCgTypeId id) {
+ return cg_type_sized_id(c, id);
+}
+
+int kit_cg_func_result_has_value(KitCompiler* c, KitCgFuncResult result) {
+ return result.type != KIT_CG_TYPE_NONE &&
+ !kit_cg_type_is_void(c, result.type);
+}
+
KitCgTypeKind kit_cg_type_kind(KitCompiler* c, KitCgTypeId id) {
const CgType* ty = cg_type_get(c, id);
return ty ? ty->kind : KIT_CG_TYPE_VOID;
}
+static KitCgStorageKind cg_storage_kind_from_abi(const CgType* ty,
+ ABITypeInfo ti) {
+ switch ((ABIScalarKind)ti.scalar_kind) {
+ case ABI_SC_VOID:
+ if (ty &&
+ (ty->kind == KIT_CG_TYPE_ARRAY || ty->kind == KIT_CG_TYPE_RECORD))
+ return KIT_CG_STORAGE_AGGREGATE;
+ return KIT_CG_STORAGE_VOID;
+ case ABI_SC_BOOL:
+ return KIT_CG_STORAGE_BOOL;
+ case ABI_SC_INT:
+ return KIT_CG_STORAGE_INT;
+ case ABI_SC_FLOAT:
+ return KIT_CG_STORAGE_FLOAT;
+ case ABI_SC_PTR:
+ return KIT_CG_STORAGE_PTR;
+ }
+ return KIT_CG_STORAGE_VOID;
+}
+
+static uint16_t cg_type_scalar_width(Compiler* c, const CgType* ty) {
+ if (!ty) return 0;
+ switch (ty->kind) {
+ case KIT_CG_TYPE_BOOL:
+ case KIT_CG_TYPE_INT:
+ return (uint16_t)ty->integer.width;
+ case KIT_CG_TYPE_FLOAT:
+ return (uint16_t)ty->fp.width;
+ case KIT_CG_TYPE_ENUM:
+ return (uint16_t)kit_cg_type_int_width((KitCompiler*)c, ty->enum_.base);
+ default:
+ return 0;
+ }
+}
+
+KitStatus kit_cg_type_info(KitCompiler* kc, KitCgTypeId id,
+ KitCgTypeInfo* out) {
+ Compiler* c = (Compiler*)kc;
+ const CgType* exact;
+ const CgType* storage;
+ KitCgTypeId storage_id;
+ ABITypeInfo ti;
+ if (!out) return KIT_INVALID;
+ memset(out, 0, sizeof(*out));
+ exact = cg_type_get(c, id);
+ if (!exact) return KIT_INVALID;
+ storage_id = api_unalias_type(c, id);
+ storage = cg_type_get(c, storage_id);
+ if (!storage) return KIT_INVALID;
+
+ out->id = id;
+ out->storage_id = storage_id;
+ out->kind = exact->kind;
+ if (id <= KIT_CG_BUILTIN_COUNT) out->flags |= KIT_CG_TYPEF_BUILTIN;
+ if (exact->kind == KIT_CG_TYPE_RECORD || exact->kind == KIT_CG_TYPE_ENUM ||
+ exact->kind == KIT_CG_TYPE_ALIAS)
+ out->flags |= KIT_CG_TYPEF_NOMINAL;
+ if (cg_type_complete_id(c, id)) out->flags |= KIT_CG_TYPEF_COMPLETE;
+ if (cg_type_sized_id(c, id)) out->flags |= KIT_CG_TYPEF_SIZED;
+
+ ti = abi_cg_type_info(c->abi, storage_id);
+ if (ti.align || storage->kind == KIT_CG_TYPE_VOID) {
+ out->layout.size = ti.size;
+ out->layout.align = ti.align;
+ out->layout.scalar_width = cg_type_scalar_width(c, storage);
+ out->layout.storage_kind = (uint8_t)cg_storage_kind_from_abi(storage, ti);
+ out->layout.valid = 1;
+ }
+ return KIT_OK;
+}
+
+static int cg_type_same_storage_rec(Compiler* c, KitCgTypeId a, KitCgTypeId b) {
+ const CgType* ta;
+ const CgType* tb;
+ a = api_unalias_type(c, a);
+ b = api_unalias_type(c, b);
+ if (!a || !b) return 0;
+ if (a == b) return 1;
+ ta = cg_type_get(c, a);
+ tb = cg_type_get(c, b);
+ if (!ta || !tb) return 0;
+ if (ta->kind == KIT_CG_TYPE_ENUM)
+ return cg_type_same_storage_rec(c, ta->enum_.base, b);
+ if (tb->kind == KIT_CG_TYPE_ENUM)
+ return cg_type_same_storage_rec(c, a, tb->enum_.base);
+ if (ta->kind != tb->kind) return 0;
+ switch (ta->kind) {
+ case KIT_CG_TYPE_VOID:
+ case KIT_CG_TYPE_BOOL:
+ case KIT_CG_TYPE_INT:
+ case KIT_CG_TYPE_FLOAT: {
+ ABITypeInfo ai = abi_cg_type_info(c->abi, a);
+ ABITypeInfo bi = abi_cg_type_info(c->abi, b);
+ return ai.scalar_kind == bi.scalar_kind && ai.size == bi.size &&
+ ai.align == bi.align;
+ }
+ case KIT_CG_TYPE_PTR:
+ return ta->ptr.address_space == tb->ptr.address_space &&
+ cg_type_same_storage_rec(c, ta->ptr.pointee, tb->ptr.pointee);
+ case KIT_CG_TYPE_ARRAY:
+ return ta->array.count == tb->array.count &&
+ cg_type_same_storage_rec(c, ta->array.elem, tb->array.elem);
+ case KIT_CG_TYPE_FUNC:
+ if (ta->func.nparams != tb->func.nparams ||
+ ta->func.call_conv != tb->func.call_conv ||
+ ta->func.abi_variadic != tb->func.abi_variadic ||
+ memcmp(&ta->func.result.attrs, &tb->func.result.attrs,
+ sizeof(ta->func.result.attrs)) != 0)
+ return 0;
+ if (!cg_type_same_storage_rec(c, ta->func.result.type,
+ tb->func.result.type))
+ return 0;
+ for (u32 i = 0; i < ta->func.nparams; ++i) {
+ if (memcmp(&ta->func.params[i].attrs, &tb->func.params[i].attrs,
+ sizeof(ta->func.params[i].attrs)) != 0)
+ return 0;
+ if (!cg_type_same_storage_rec(c, ta->func.params[i].type,
+ tb->func.params[i].type))
+ return 0;
+ }
+ return 1;
+ case KIT_CG_TYPE_RECORD:
+ return 0;
+ case KIT_CG_TYPE_ALIAS:
+ return 0;
+ case KIT_CG_TYPE_ENUM:
+ return 0;
+ case KIT_CG_TYPE_VARARG_STATE: {
+ ABITypeInfo ai = abi_cg_type_info(c->abi, a);
+ ABITypeInfo bi = abi_cg_type_info(c->abi, b);
+ return ai.size == bi.size && ai.align == bi.align;
+ }
+ }
+ return 0;
+}
+
+int kit_cg_type_same_storage(KitCompiler* c, KitCgTypeId a, KitCgTypeId b) {
+ return cg_type_same_storage_rec((Compiler*)c, a, b);
+}
+
uint32_t kit_cg_type_int_width(KitCompiler* c, KitCgTypeId id) {
const CgType* ty = cg_type_get(c, id);
if (!ty) return 0;
@@ -1078,7 +1325,7 @@ uint32_t kit_cg_type_int_width(KitCompiler* c, KitCgTypeId id) {
return ty->integer.width;
}
if (ty->kind == KIT_CG_TYPE_ENUM) {
- return (uint32_t)ty->size * 8u;
+ return kit_cg_type_int_width(c, ty->enum_.base);
}
if (ty->kind == KIT_CG_TYPE_ALIAS) {
return kit_cg_type_int_width(c, ty->alias.base);
@@ -1153,9 +1400,21 @@ int kit_cg_type_func_is_variadic(KitCompiler* c, KitCgTypeId id) {
return ty && ty->kind == KIT_CG_TYPE_FUNC && ty->func.abi_variadic;
}
+KitSym kit_cg_type_record_tag(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return (ty && ty->kind == KIT_CG_TYPE_RECORD) ? ty->record.tag : 0;
+}
+
+int kit_cg_type_record_is_union(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return ty && ty->kind == KIT_CG_TYPE_RECORD && ty->record.is_union;
+}
+
uint32_t kit_cg_type_record_nfields(KitCompiler* c, KitCgTypeId id) {
const CgType* ty = cg_type_get(c, id);
- return (ty && ty->kind == KIT_CG_TYPE_RECORD) ? ty->record.nfields : 0;
+ return (ty && ty->kind == KIT_CG_TYPE_RECORD && cg_type_record_complete(ty))
+ ? ty->record.nfields
+ : 0;
}
KitStatus kit_cg_type_record_field(KitCompiler* c, KitCgTypeId id,
@@ -1163,7 +1422,8 @@ KitStatus kit_cg_type_record_field(KitCompiler* c, KitCgTypeId id,
uint64_t* offset_out) {
const CgType* ty = cg_type_get(c, id);
const CgTypeField* f;
- if (!ty || ty->kind != KIT_CG_TYPE_RECORD || index >= ty->record.nfields) {
+ if (!ty || ty->kind != KIT_CG_TYPE_RECORD || !cg_type_record_complete(ty) ||
+ index >= ty->record.nfields) {
return KIT_NOT_FOUND;
}
f = &ty->record.fields[index];
@@ -1182,6 +1442,42 @@ KitStatus kit_cg_type_record_field(KitCompiler* c, KitCgTypeId id,
return KIT_OK;
}
+KitSym kit_cg_type_enum_tag(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return (ty && ty->kind == KIT_CG_TYPE_ENUM) ? ty->enum_.tag : 0;
+}
+
+KitCgTypeId kit_cg_type_enum_base(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return (ty && ty->kind == KIT_CG_TYPE_ENUM) ? ty->enum_.base
+ : KIT_CG_TYPE_NONE;
+}
+
+uint32_t kit_cg_type_enum_nvalues(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return (ty && ty->kind == KIT_CG_TYPE_ENUM) ? ty->enum_.nvalues : 0;
+}
+
+KitStatus kit_cg_type_enum_value(KitCompiler* c, KitCgTypeId id, uint32_t index,
+ KitCgEnumValue* out) {
+ const CgType* ty = cg_type_get(c, id);
+ if (!ty || ty->kind != KIT_CG_TYPE_ENUM || index >= ty->enum_.nvalues)
+ return KIT_NOT_FOUND;
+ if (out) *out = ty->enum_.values[index];
+ return KIT_OK;
+}
+
+KitSym kit_cg_type_alias_name(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return (ty && ty->kind == KIT_CG_TYPE_ALIAS) ? ty->alias.name : 0;
+}
+
+KitCgTypeId kit_cg_type_alias_base(KitCompiler* c, KitCgTypeId id) {
+ const CgType* ty = cg_type_get(c, id);
+ return (ty && ty->kind == KIT_CG_TYPE_ALIAS) ? ty->alias.base
+ : KIT_CG_TYPE_NONE;
+}
+
int kit_cg_target_supports_call_conv(KitCompiler* c, KitCgCallConv cc) {
const ArchImpl* a;
if (!c) return 0;
@@ -1270,7 +1566,7 @@ void cg_api_fini(Compiler* c) {
s = (CgApiState*)c->cg_api;
CgApiTypes_fini(&s->types);
CgPtrMap_fini(&s->ptr_index);
- CgArrayMap_fini(&s->array_index);
+ CgArraySet_fini(&s->array_index);
CgFuncSet_fini(&s->func_index);
s->heap->free(s->heap, s, sizeof(*s));
c->cg_api = NULL;
diff --git a/src/cg/type.h b/src/cg/type.h
@@ -39,7 +39,7 @@ typedef struct CgType {
u64 count;
} array;
struct {
- KitCgFuncResult result; /* result.type == KIT_CG_TYPE_NONE == void */
+ KitCgFuncResult result; /* void builtin means no value */
KitCgFuncParam* params;
u32 nparams;
KitCgCallConv call_conv;
@@ -66,6 +66,8 @@ typedef struct CgType {
};
} CgType;
+#define CG_TYPE_RECORD_COMPLETE 0x1u
+
const CgType* cg_type_get(Compiler*, KitCgTypeId);
uint64_t cg_type_size(Compiler*, KitCgTypeId);
uint32_t cg_type_align(Compiler*, KitCgTypeId);
@@ -74,14 +76,6 @@ int cg_type_is_float(Compiler*, KitCgTypeId);
int cg_type_is_ptr(Compiler*, KitCgTypeId);
int cg_type_is_record(Compiler*, KitCgTypeId);
-enum {
- CG_API_TYPE_SEG_SHIFT = 6,
- CG_API_TYPE_SEG_SIZE = 1u << CG_API_TYPE_SEG_SHIFT,
- CG_API_TYPE_SEG_MASK = CG_API_TYPE_SEG_SIZE - 1u,
- CG_API_TYPE_BUILTIN_SEG = 1u,
- CG_API_TYPE_USER_SEG_BIAS = 2u,
-};
-
KitCgTypeId builtin_id(KitCgBuiltinType);
KitCgTypeId resolve_type(Compiler*, KitCgTypeId);
KitCgTypeId api_unalias_type(Compiler*, KitCgTypeId);
@@ -92,8 +86,8 @@ int cg_type_is_aggregate(Compiler*, KitCgTypeId);
* cg_type_is_* predicates are (bits & API_PRED_*) tests over this set; a site
* that asks several predicates of the SAME id reads the bitset once via
* api_type_pred_bits and tests the masks locally (one decode, not N). The
- * membership rules MUST match cg_type_is_* exactly: INT covers INT|BOOL|ENUM and
- * AGGREGATE == RECORD. */
+ * membership rules MUST match cg_type_is_* exactly: INT covers INT|BOOL|ENUM
+ * and AGGREGATE == RECORD. */
#define API_PRED_INT 0x01u
#define API_PRED_FLOAT 0x02u
#define API_PRED_PTR 0x04u
@@ -118,10 +112,10 @@ int api_is_wide8_scalar_type(Compiler*, KitCgTypeId);
* Pure function of the type and the target's fixed float-ABI / split-lane
* policy. WK_NARROW == 0 is the common fast path. */
typedef enum ApiWideKind {
- WK_NARROW = 0, /* not wide / not soft-float: the common fast path */
- WK_I128, /* 128-bit integer (api_is_i128_type) */
- WK_F128, /* 128-bit float / long double (api_is_f128_type) */
- WK_WIDE8, /* int split into two 32-bit lanes (api_is_wide8_scalar_type) */
+ WK_NARROW = 0, /* not wide / not soft-float: the common fast path */
+ WK_I128, /* 128-bit integer (api_is_i128_type) */
+ WK_F128, /* 128-bit float / long double (api_is_f128_type) */
+ WK_WIDE8, /* int split into two 32-bit lanes (api_is_wide8_scalar_type) */
WK_SOFT_DOUBLE, /* f64 on a target without hardware double */
WK_SOFT_SINGLE, /* f32 on a pure-soft target (no FP unit) */
} ApiWideKind;
@@ -129,8 +123,8 @@ typedef enum ApiWideKind {
/* api_type_class packs a type's codegen classification into one byte: the
* ApiWideKind in the low bits plus an aggregate-place bit. Both are pure
* functions of (type, target), so the result is memoized per type id (builtins
- * precomputed at cg_api init, user types lazily) rather than re-derived on every
- * value push. */
+ * precomputed at cg_api init, user types lazily) rather than re-derived on
+ * every value push. */
#define API_TYPE_CLASS_WIDE_MASK 0x07u
#define API_TYPE_CLASS_AGGREGATE 0x08u
u8 api_type_class(Compiler*, KitCgTypeId);
@@ -149,12 +143,12 @@ void api_type_layout_put(Compiler*, KitCgTypeId, u32 size, u32 align,
KitCgTypeId cg_type_ptr_to(Compiler*, KitCgTypeId);
KitCgTypeId cg_type_pointee(Compiler*, KitCgTypeId);
-/* The function's result type, or the VOID builtin when it returns nothing. */
+/* The function's result type. A no-value return is the VOID builtin. */
KitCgTypeId cg_type_func_ret_id(Compiler*, KitCgTypeId);
-/* The function's raw result type, or KIT_CG_TYPE_NONE when it returns nothing
- * (the "has a result" predicate; cg_type_func_ret_id maps none -> VOID). */
+/* Same as cg_type_func_ret_id, kept for callers that distinguish the
+ * single-result path from the full KitCgFuncResult descriptor. */
KitCgTypeId cg_type_func_result_id(Compiler*, KitCgTypeId);
-/* First-result type (or VOID) given a resolved KIT_CG_TYPE_FUNC CgType. For
+/* First-result type given a resolved KIT_CG_TYPE_FUNC CgType. For
* the single-result/void consumers (ABI classification, source backends) that
* already hold the CgType. */
KitCgTypeId cg_func_ret_type(const CgType* fnty);
diff --git a/src/cg/value.c b/src/cg/value.c
@@ -2,17 +2,17 @@
/* The cached wide-class tag (ApiBitField.wide_kind) reuses the bit-field
* member's trailing pad: ApiBitField stays at its original 12-byte footprint
- * (u16 + u16 + u32 + u8 bit_signed + u8 wide_kind + u8 pad[2]), and the node has
- * no slack past it, so the value-stack node size is unchanged. */
+ * (u16 + u16 + u32 + u8 bit_signed + u8 wide_kind + u8 pad[2]). Inline language
+ * facts fit by packing the CG-owned state bytes into ApiSValue.flags. */
_Static_assert(sizeof(ApiBitField) == 12,
"wide_kind must reuse ApiBitField pad, not grow it");
-_Static_assert(offsetof(ApiSValue, bitfield) + sizeof(ApiBitField) ==
- sizeof(ApiSValue),
- "ApiSValue size must be unchanged by the cached wide_kind tag");
/* The 64-byte delayed cmp/arith payload lives off the node behind a pointer
* (see ApiDelayed); the value-stack node must stay small. */
-_Static_assert(sizeof(ApiSValue) <= 64,
- "delayed payload must be off-node; ApiSValue stays small");
+_Static_assert(sizeof(ApiSValue) == 64,
+ "ApiSValue must stay exactly one 64-byte stack node");
+_Static_assert(offsetof(ApiSValue, flags) + sizeof(((ApiSValue*)0)->flags) ==
+ sizeof(ApiSValue),
+ "ApiSValue flags should close the packed 64-byte node");
/* The operand/value constructors run on the hottest per-operand codegen path.
* Each uses a designated compound literal rather than memset + field stores: it
@@ -35,20 +35,18 @@ Operand api_op_global(ObjSymId sym, i64 addend, KitCgTypeId ty) {
}
Operand api_op_indirect(CGLocal base, i32 ofs, KitCgTypeId ty) {
- return (Operand){
- .kind = OPK_INDIRECT,
- .type = ty,
- .v.ind = {.base = base, .index = CG_LOCAL_NONE, .ofs = ofs}};
+ return (Operand){.kind = OPK_INDIRECT,
+ .type = ty,
+ .v.ind = {.base = base, .index = CG_LOCAL_NONE, .ofs = ofs}};
}
Operand api_op_indirect_indexed(CGLocal base, CGLocal index, u8 log2_scale,
i32 ofs, KitCgTypeId ty) {
- return (Operand){.kind = OPK_INDIRECT,
- .type = ty,
- .v.ind = {.base = base,
- .index = index,
- .log2_scale = log2_scale,
- .ofs = ofs}};
+ return (Operand){
+ .kind = OPK_INDIRECT,
+ .type = ty,
+ .v.ind = {
+ .base = base, .index = index, .log2_scale = log2_scale, .ofs = ofs}};
}
u8 api_residency_for(const Operand* o) {
@@ -61,23 +59,23 @@ ApiSValue api_make_sv(Operand op, KitCgTypeId ty) {
* 64-byte delayed union, the bitfield rider, the flags) still zero-fill, but
* the compiler emits direct stores for the live fields and skips the
* out-of-line memset of the whole node on this hot per-operand path. */
- return (ApiSValue){.kind = SV_OPERAND,
- .op = op,
- .type = ty,
- .res = api_residency_for(&op),
- .source_local = KIT_CG_LOCAL_NONE};
+ return (ApiSValue){
+ .op = op,
+ .type = ty,
+ .source_local = KIT_CG_LOCAL_NONE,
+ .flags = API_SV_PACK(SV_OPERAND, api_residency_for(&op), 0, 0)};
}
ApiSValue api_make_lv(Operand op, KitCgTypeId ty) {
ApiSValue sv = api_make_sv(op, ty);
- sv.lvalue = 1;
+ api_sv_set_lvalue(&sv, 1);
return sv;
}
ApiSValue api_make_sv_with_local_ownership(Operand op, KitCgTypeId ty,
int owned) {
ApiSValue sv = api_make_sv(op, ty);
- if (op.kind == OPK_LOCAL && !owned) sv.res = RES_FIXED_LOCAL;
+ if (op.kind == OPK_LOCAL && !owned) api_sv_set_res(&sv, RES_FIXED_LOCAL);
return sv;
}
@@ -91,11 +89,11 @@ int api_operand_can_address(const Operand* o) {
}
int api_sv_op_is(const ApiSValue* sv, OpKind kind) {
- return sv->kind == SV_OPERAND && sv->op.kind == kind;
+ return api_sv_kind(sv) == SV_OPERAND && sv->op.kind == kind;
}
int api_sv_op_is_local_or_imm(const ApiSValue* sv) {
- return sv->kind == SV_OPERAND &&
+ return api_sv_kind(sv) == SV_OPERAND &&
(sv->op.kind == OPK_IMM || sv->op.kind == OPK_LOCAL);
}
@@ -111,7 +109,7 @@ int api_sv_op_is_local_or_imm(const ApiSValue* sv) {
* kit_cg_field — and source_local only co-occurs with OPK_LOCAL, so
* api_operand_can_address already covers both. */
int api_is_lvalue_sv(const ApiSValue* sv) {
- return sv->lvalue && sv->kind == SV_OPERAND &&
+ return api_sv_lvalue_flag(sv) && api_sv_kind(sv) == SV_OPERAND &&
api_operand_can_address(&sv->op);
}
@@ -143,14 +141,22 @@ void api_stack_grow(KitCg* g, u32 want) {
Heap* h = g->c->ctx->heap;
u32 cap = g->cap;
ApiSValue* nb;
+ ApiConstValue* nc;
if (cap >= want) return;
while (cap < want) cap = cap ? cap * 2u : API_CG_STACK_INITIAL;
nb = (ApiSValue*)h->alloc(h, sizeof(ApiSValue) * cap, _Alignof(ApiSValue));
+ nc = (ApiConstValue*)h->alloc(h, sizeof(ApiConstValue) * cap,
+ _Alignof(ApiConstValue));
if (g->stack) {
memcpy(nb, g->stack, sizeof(ApiSValue) * g->sp);
h->free(h, g->stack, sizeof(ApiSValue) * g->cap);
}
+ if (g->const_stack) {
+ memcpy(nc, g->const_stack, sizeof(ApiConstValue) * g->sp);
+ h->free(h, g->const_stack, sizeof(ApiConstValue) * g->cap);
+ }
g->stack = nb;
+ g->const_stack = nc;
g->cap = cap;
}
@@ -164,11 +170,12 @@ void api_push(KitCg* g, ApiSValue v) {
* wide/soft-float dispatch class — both pure functions of the type, computed
* once per type id rather than re-derived (cg_type_get + alias-chase + ABI
* round trips) on every push. */
- u8 cls = api_type_class(g->c, ty);
+ u8 cls = ty ? api_type_class(g->c, ty) : 0;
/* An aggregate (record) can only ever be a PLACE: it is addressed, loaded,
* and passed by SRET/BYVAL/BYREF, never materialized as a scalar VALUE. Catch
* any aggregate VALUE at the point it would enter the stack. i128/f128 are
- * scalars (not aggregates), so they remain valid VALUEs and are unaffected. */
+ * scalars (not aggregates), so they remain valid VALUEs and are unaffected.
+ */
if ((cls & API_TYPE_CLASS_AGGREGATE) && !api_is_lvalue_sv(&v)) {
compiler_panic(g->c, g->cur_loc,
"KitCg: aggregate must be a place, not a value; load the "
@@ -180,8 +187,10 @@ void api_push(KitCg* g, ApiSValue v) {
* stays valid for every later reader. */
v.bitfield.wide_kind = cls & API_TYPE_CLASS_WIDE_MASK;
api_stack_grow(g, g->sp + 1);
- g->stack[g->sp++] = v;
- api_sv_adjust_refs(g, &v, +1);
+ g->stack[g->sp] = v;
+ g->const_stack[g->sp] = api_const_from_sv(g, &v);
+ g->sp++;
+ if (!api_unevaluated(g)) api_sv_adjust_refs(g, &v, +1);
}
ApiSValue api_pop(KitCg* g) {
@@ -190,14 +199,15 @@ ApiSValue api_pop(KitCg* g) {
compiler_panic(g->c, g->cur_loc, "KitCg: stack underflow");
}
r = g->stack[--g->sp];
- api_sv_adjust_refs(g, &r, -1);
+ if (!api_unevaluated(g)) api_sv_adjust_refs(g, &r, -1);
return r;
}
/* ---- local helpers ---- */
CGLocal api_local_of_sv(const ApiSValue* sv) {
- if (sv->kind == SV_ARITH || sv->kind == SV_CMP) return (CGLocal)CG_LOCAL_NONE;
+ if (api_sv_kind(sv) == SV_ARITH || api_sv_kind(sv) == SV_CMP)
+ return (CGLocal)CG_LOCAL_NONE;
if (sv->op.kind == OPK_LOCAL) return sv->op.v.local;
if (sv->op.kind == OPK_INDIRECT) return sv->op.v.ind.base;
return (CGLocal)CG_LOCAL_NONE;
@@ -223,11 +233,13 @@ KitCgTypeId api_owned_local_type(KitCg* g, const ApiSValue* sv) {
CGLocal api_alloc_temp_local(KitCg* g, KitCgTypeId ty) {
CGLocalDesc d;
CGLocal local;
+ if (api_unevaluated(g)) return CG_LOCAL_NONE;
memset(&d, 0, sizeof d);
d.type = ty;
/* A compiler temporary: its home is reclaimable once the value stack drops it
* at a statement boundary (kit_cg_reclaim_temps), so the single-pass frame is
- * bounded by max-simultaneous-live temps rather than a per-subexpression sum. */
+ * bounded by max-simultaneous-live temps rather than a per-subexpression sum.
+ */
d.flags |= CG_LOCAL_TRANSIENT;
if (ty) {
ABITypeInfo ti = abi_cg_type_info(g->c->abi, ty);
@@ -360,14 +372,16 @@ void api_validate_memory_value(KitCg* g, const char* who, KitCgTypeId access_ty,
}
int api_sv_owns_operand_local(const ApiSValue* sv, const Operand* op) {
- return sv->res == RES_LOCAL && op->kind == OPK_LOCAL &&
+ return api_sv_res(sv) == RES_LOCAL && op->kind == OPK_LOCAL &&
sv->op.kind == OPK_LOCAL && sv->op.v.local == op->v.local;
}
void api_ensure_local(KitCg* g, ApiSValue* sv) {
- if (sv->kind == SV_CMP) {
+ if (api_unevaluated(g)) return;
+ if (api_sv_kind(sv) == SV_CMP) {
KitCgTypeId ty = api_sv_type(sv);
- KitCgTypeId uty = api_unalias_type(g->c, ty); /* compute the dst terminal once */
+ KitCgTypeId uty =
+ api_unalias_type(g->c, ty); /* compute the dst terminal once */
Operand dst;
if (sv->delayed->cmp.a_owned && sv->delayed->cmp.a.kind == OPK_LOCAL &&
api_unalias_type(g->c, sv->delayed->cmp.a.type) == uty) {
@@ -384,9 +398,10 @@ void api_ensure_local(KitCg* g, ApiSValue* sv) {
api_materialize_cmp_to(g, sv, dst);
return;
}
- if (sv->kind == SV_ARITH) {
+ if (api_sv_kind(sv) == SV_ARITH) {
KitCgTypeId ty = api_sv_type(sv);
- KitCgTypeId uty = api_unalias_type(g->c, ty); /* compute the dst terminal once */
+ KitCgTypeId uty =
+ api_unalias_type(g->c, ty); /* compute the dst terminal once */
Operand dst;
if (sv->delayed->arith.a_owned && sv->delayed->arith.a.kind == OPK_LOCAL &&
api_unalias_type(g->c, sv->delayed->arith.a.type) == uty) {
@@ -409,6 +424,7 @@ void api_ensure_local(KitCg* g, ApiSValue* sv) {
Operand api_force_local(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
CgTarget* T = g->target;
ty = api_unalias_type(g->c, ty);
+ if (api_unevaluated(g)) return api_op_imm(0, ty);
api_ensure_local(g, v);
if (v->op.kind == OPK_LOCAL && !api_is_lvalue_sv(v)) {
return v->op;
@@ -425,7 +441,7 @@ Operand api_force_local(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
compiler_panic(g->c, g->cur_loc, "KitCg: cannot force operand to local");
}
v->op = dst;
- v->res = RES_LOCAL;
+ api_sv_set_res(v, RES_LOCAL);
return dst;
}
@@ -435,12 +451,12 @@ Operand api_force_local_unless_imm(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
}
void api_release(KitCg* g, ApiSValue* sv) {
- if (sv->kind == SV_CMP) {
+ if (api_sv_kind(sv) == SV_CMP) {
api_release_cmp(g, sv);
- } else if (sv->kind == SV_ARITH) {
+ } else if (api_sv_kind(sv) == SV_ARITH) {
api_release_arith(g, sv);
}
- sv->res = RES_INHERENT;
+ api_sv_set_res(sv, RES_INHERENT);
}
/* ---- -O0 transient liveness (refcount + confirming scan) ---- */
@@ -454,8 +470,8 @@ int api_coalesce_on(KitCg* g) {
return g->coalesce;
}
-/* Mark `local` (a fresh api_alloc_temp_local handle) as this function's temp and
- * zero its live-reference count. */
+/* Mark `local` (a fresh api_alloc_temp_local handle) as this function's temp
+ * and zero its live-reference count. */
static void api_track_temp(KitCg* g, CGLocal local) {
Heap* h = g->c->ctx->heap;
u32 idx = (u32)local;
@@ -514,13 +530,13 @@ static void api_op_adjust_refs(KitCg* g, const Operand* op, int delta) {
/* All temp locals an SValue references: its operand plus, for a delayed
* cmp/arith, its pending operands. */
static void api_sv_adjust_refs(KitCg* g, const ApiSValue* sv, int delta) {
- if (sv->kind == SV_OPERAND) {
+ if (api_sv_kind(sv) == SV_OPERAND) {
api_op_adjust_refs(g, &sv->op, delta);
} else if (sv->delayed) {
- if (sv->kind == SV_CMP) {
+ if (api_sv_kind(sv) == SV_CMP) {
api_op_adjust_refs(g, &sv->delayed->cmp.a, delta);
api_op_adjust_refs(g, &sv->delayed->cmp.b, delta);
- } else if (sv->kind == SV_ARITH) {
+ } else if (api_sv_kind(sv) == SV_ARITH) {
api_op_adjust_refs(g, &sv->delayed->arith.a, delta);
if (sv->delayed->arith.kind == API_DELAYED_BINOP)
api_op_adjust_refs(g, &sv->delayed->arith.b, delta);
@@ -536,31 +552,32 @@ void api_reseat_end(KitCg* g, const ApiSValue* sv) {
}
/* Ground-truth scan: how many live value-stack entries reference temp `local`.
- * The arbiter when the incremental count reads 0 (so a reseat-accounting gap can
- * only cost a missed optimization, never a miscompile). */
+ * The arbiter when the incremental count reads 0 (so a reseat-accounting gap
+ * can only cost a missed optimization, never a miscompile). */
static u32 api_temp_scan_refs(KitCg* g, CGLocal local) {
u32 n = 0;
for (u32 i = 0; i < g->sp; ++i) {
const ApiSValue* sv = &g->stack[i];
const Operand* op = &sv->op;
- if (sv->kind == SV_OPERAND) {
- if (op->kind == OPK_LOCAL && op->v.local == local) n++;
+ if (api_sv_kind(sv) == SV_OPERAND) {
+ if (op->kind == OPK_LOCAL && op->v.local == local)
+ n++;
else if (op->kind == OPK_INDIRECT &&
(op->v.ind.base == local || op->v.ind.index == local))
n++;
} else if (sv->delayed) {
const ApiDelayed* d = sv->delayed;
- const Operand* a = sv->kind == SV_CMP ? &d->cmp.a : &d->arith.a;
- const Operand* b = sv->kind == SV_CMP ? &d->cmp.b : &d->arith.b;
- int has_b = sv->kind == SV_CMP || d->arith.kind == API_DELAYED_BINOP;
+ const Operand* a = api_sv_kind(sv) == SV_CMP ? &d->cmp.a : &d->arith.a;
+ const Operand* b = api_sv_kind(sv) == SV_CMP ? &d->cmp.b : &d->arith.b;
+ int has_b =
+ api_sv_kind(sv) == SV_CMP || d->arith.kind == API_DELAYED_BINOP;
if ((a->kind == OPK_LOCAL && a->v.local == local) ||
(a->kind == OPK_INDIRECT &&
(a->v.ind.base == local || a->v.ind.index == local)))
n++;
- if (has_b &&
- ((b->kind == OPK_LOCAL && b->v.local == local) ||
- (b->kind == OPK_INDIRECT &&
- (b->v.ind.base == local || b->v.ind.index == local))))
+ if (has_b && ((b->kind == OPK_LOCAL && b->v.local == local) ||
+ (b->kind == OPK_INDIRECT &&
+ (b->v.ind.base == local || b->v.ind.index == local))))
n++;
}
}
@@ -569,8 +586,9 @@ static u32 api_temp_scan_refs(KitCg* g, CGLocal local) {
int api_temp_dead(KitCg* g, CGLocal local) {
if (local == CG_LOCAL_NONE || !api_handle_is_temp(g, local)) return 0;
- if (g->local_refs[(u32)local] > 0) return 0; /* fast reject: still referenced */
- return api_temp_scan_refs(g, local) == 0; /* confirm before acting */
+ if (g->local_refs[(u32)local] > 0)
+ return 0; /* fast reject: still referenced */
+ return api_temp_scan_refs(g, local) == 0; /* confirm before acting */
}
Operand api_op_kill_if_dead(KitCg* g, Operand op, Operand dst) {
@@ -704,6 +722,7 @@ Operand api_lvalue_addr(KitCg* g, ApiSValue* v, KitCgTypeId pty) {
ApiSourceLocal* rec;
CGLocal r;
Operand dst;
+ if (api_unevaluated(g)) return api_op_imm(0, pty);
api_local_const_address_taken(g, v->source_local);
api_ensure_local(g, v);
if (!api_is_lvalue_sv(v)) {
diff --git a/src/cg/wide.c b/src/cg/wide.c
@@ -46,6 +46,21 @@ ApiSValue api_make_wide16_int_const(KitCg* g, i64 value, KitCgTypeId ty) {
return api_make_sv(api_op_local(local, ty), ty);
}
+ApiSValue api_make_wide16_int_const_bits(KitCg* g, u64 lo, u64 hi,
+ KitCgTypeId ty) {
+ u8 bytes[16];
+ CGLocal local;
+ for (u32 i = 0; i < 8; ++i) {
+ u32 lo_idx = g->c->target.big_endian ? 15u - i : i;
+ u32 hi_idx = g->c->target.big_endian ? 7u - i : 8u + i;
+ bytes[lo_idx] = (u8)(lo >> (i * 8u));
+ bytes[hi_idx] = (u8)(hi >> (i * 8u));
+ }
+ local = api_f128_temp_local(g, ty);
+ api_store_f128_bytes(g, local, ty, bytes);
+ return api_make_sv(api_op_local(local, ty), ty);
+}
+
void api_store_f128_bytes(KitCg* g, CGLocal local, KitCgTypeId ty,
const u8 bytes[16]) {
KitCgTypeId i64_ty = builtin_id(KIT_CG_BUILTIN_I64);
@@ -208,10 +223,10 @@ Operand api_wide8_addr(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
* no-op (kind is now SV_OPERAND), so the flag survives. An operand that is
* already a place is left untouched by api_ensure_local and flows through as
* before. */
- if (lv.kind != SV_OPERAND) api_ensure_local(g, &lv);
+ if (api_sv_kind(&lv) != SV_OPERAND) api_ensure_local(g, &lv);
lv.type = ty;
lv.op.type = ty;
- lv.lvalue = 1;
+ api_sv_set_lvalue(&lv, 1);
return api_lvalue_addr(g, &lv, cg_type_ptr_to(g->c, ty));
}
@@ -247,14 +262,14 @@ void api_wide8_store_lane(KitCg* g, Operand addr, i32 off, Operand val) {
ApiSValue api_wide16_materialize_lvalue(KitCg* g, ApiSValue* v,
KitCgTypeId ty) {
if (v->op.kind == OPK_LOCAL) {
- v->lvalue = 1;
+ api_sv_set_lvalue(v, 1);
return *v;
}
if (v->op.kind == OPK_INDIRECT) {
ApiSValue out = *v;
out.type = ty;
out.op.type = ty;
- out.lvalue = 1;
+ api_sv_set_lvalue(&out, 1);
return out;
}
if (v->op.kind == OPK_GLOBAL) {
@@ -279,7 +294,7 @@ ApiSValue api_wide16_materialize_lvalue(KitCg* g, ApiSValue* v,
compiler_panic(
g->c, g->cur_loc,
"KitCg: 16-byte scalar value is not addressable (kind %u, op %u)",
- (unsigned)v->kind, (unsigned)v->op.kind);
+ (unsigned)api_sv_kind(v), (unsigned)v->op.kind);
return *v;
}
@@ -316,6 +331,17 @@ void api_runtime_call_values(KitCg* g, const char* name, KitCgTypeId ret,
const KitCgTypeId* params, u32 nparams,
ApiSValue* args) {
KitCgCallAttrs attrs;
+ if (api_unevaluated(g)) {
+ (void)name;
+ (void)params;
+ for (u32 i = 0; i < nparams; ++i)
+ if (args) api_release(g, &args[i]);
+ if (ret != KIT_CG_TYPE_NONE) {
+ api_push(g, api_uneval_value(g, ret));
+ api_const_set_top(g, api_const_unknown(ret));
+ }
+ return;
+ }
KitCgSym sym = api_runtime_helper(g, name, ret, params, nparams);
memset(&attrs, 0, sizeof attrs);
for (u32 i = 0; i < nparams; ++i) api_push(g, args[i]);
diff --git a/src/emu/cpu.c b/src/emu/cpu.c
@@ -121,20 +121,18 @@ EmuCPUState* emu_thread_cpu(EmuThread* t) { return t ? t->cpu : NULL; }
KitCgTypeId emu_thread_type(Compiler* c) {
return kit_cg_type_ptr(
(KitCompiler*)c,
- kit_cg_builtin_types((KitCompiler*)c).id[KIT_CG_BUILTIN_VOID], 0);
+ kit_cg_type_builtin((KitCompiler*)c, KIT_CG_BUILTIN_VOID), 0);
}
KitCgTypeId emu_block_fn_type(Compiler* c) {
- KitCgBuiltinTypes bi;
KitCgFuncParam param;
KitCgFuncResult result;
KitCgFuncSig sig;
- bi = kit_cg_builtin_types((KitCompiler*)c);
memset(¶m, 0, sizeof(param));
param.type = emu_thread_type(c);
memset(&sig, 0, sizeof(sig));
memset(&result, 0, sizeof(result));
- result.type = bi.id[KIT_CG_BUILTIN_I64];
+ result.type = kit_cg_type_builtin((KitCompiler*)c, KIT_CG_BUILTIN_I64);
sig.result = result;
sig.params = ¶m;
sig.nparams = 1;
diff --git a/src/opt/pass_combine.c b/src/opt/pass_combine.c
@@ -6,12 +6,6 @@
#include "core/arena.h"
#include "opt/opt_internal.h"
-enum {
- COMBINE_CG_TYPE_SEG_SHIFT = 6,
- COMBINE_CG_TYPE_SEG_MASK = (1u << COMBINE_CG_TYPE_SEG_SHIFT) - 1u,
- COMBINE_CG_TYPE_BUILTIN_SEG = 1u,
-};
-
/* O1 combine, MIR-shaped (see mir-gen.c:8808-9146). One forward pass per BB
* maintains last-def / last-mem-def tracking; per-BB fixpoint loop iterates
* until no fold fires. Rewrites in this file:
@@ -558,8 +552,9 @@ static int use_after_clobber_before_redef(const Block* bl, i32 prod_idx,
/* ---- ConvKind helpers (for combine_exts) ---- */
static u32 builtin_int_bytes(KitCgTypeId t) {
- if ((t >> COMBINE_CG_TYPE_SEG_SHIFT) != COMBINE_CG_TYPE_BUILTIN_SEG) return 0;
- KitCgBuiltinType b = (KitCgBuiltinType)(t & COMBINE_CG_TYPE_SEG_MASK);
+ KitCgBuiltinType b;
+ if (t == KIT_CG_TYPE_NONE || t > (KitCgTypeId)KIT_CG_BUILTIN_COUNT) return 0;
+ b = (KitCgBuiltinType)(t - 1u);
switch (b) {
case KIT_CG_BUILTIN_BOOL:
case KIT_CG_BUILTIN_I8:
diff --git a/src/opt/pass_lower.c b/src/opt/pass_lower.c
@@ -1,3 +1,4 @@
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -9,19 +10,22 @@
#include "core/strbuf.h"
#include "opt/opt_internal.h"
-enum {
- OPT_CG_TYPE_SEG_SHIFT = 6,
- OPT_CG_TYPE_SEG_MASK = (1u << OPT_CG_TYPE_SEG_SHIFT) - 1u,
- OPT_CG_TYPE_BUILTIN_SEG = 1u,
-};
+static int direct_builtin_from_type(KitCgTypeId t, KitCgBuiltinType* out) {
+ if (t == KIT_CG_TYPE_NONE || t > (KitCgTypeId)KIT_CG_BUILTIN_COUNT) return 0;
+ *out = (KitCgBuiltinType)(t - 1u);
+ return 1;
+}
static u32 type_size_fallback(const Func* f, KitCgTypeId t) {
KitCgBuiltinType b;
if (!t) return f->opt_target.ptr_size ? f->opt_target.ptr_size : 8u;
- if ((t >> OPT_CG_TYPE_SEG_SHIFT) != OPT_CG_TYPE_BUILTIN_SEG) {
+ if (f && f->c) {
+ uint64_t size = kit_cg_type_size((KitCompiler*)f->c, t);
+ if (size && size <= UINT32_MAX) return (u32)size;
+ }
+ if (!direct_builtin_from_type(t, &b)) {
return f->opt_target.ptr_size ? f->opt_target.ptr_size : 8u;
}
- b = (KitCgBuiltinType)(t & OPT_CG_TYPE_SEG_MASK);
switch (b) {
case KIT_CG_BUILTIN_BOOL:
case KIT_CG_BUILTIN_I8:
diff --git a/test/api/abi_classify_test.c b/test/api/abi_classify_test.c
@@ -51,6 +51,10 @@ static KitCompiler* new_compiler(KitArchKind arch, KitOSKind os,
return c;
}
+static KitCgTypeId builtin(KitCompiler* c, KitCgBuiltinType which) {
+ return kit_cg_type_builtin(c, which);
+}
+
/* Build a function type `ret_ty fn(arg_ty)` and return its ABIFuncInfo. */
static const ABIFuncInfo* classify_fn(KitCompiler* c, KitCgTypeId ret_ty,
KitCgTypeId arg_ty) {
@@ -61,11 +65,9 @@ static const ABIFuncInfo* classify_fn(KitCompiler* c, KitCgTypeId ret_ty,
memset(¶m, 0, sizeof param);
param.type = arg_ty;
memset(&sig, 0, sizeof sig);
- if (ret_ty != kit_cg_builtin_types(c).id[KIT_CG_BUILTIN_VOID]) {
- memset(&sig_result, 0, sizeof sig_result);
- sig_result.type = ret_ty;
- sig.result = sig_result;
- }
+ memset(&sig_result, 0, sizeof sig_result);
+ sig_result.type = ret_ty;
+ sig.result = sig_result;
sig.params = ¶m;
sig.nparams = 1;
fn = kit_cg_type_func(c, sig);
@@ -175,17 +177,20 @@ static void expect_direct_2(const char* tag, const ABIArgInfo* ai, u8 c0, u8 c1,
static KitCgTypeId record2(KitCompiler* c, KitCgTypeId a, KitCgTypeId b) {
KitCgField f[2];
+ KitCgRecordDesc desc;
memset(f, 0, sizeof f);
f[0].type = a;
f[1].type = b;
- return kit_cg_type_record(c, 0, f, 2);
+ memset(&desc, 0, sizeof desc);
+ desc.fields = f;
+ desc.nfields = 2;
+ return kit_cg_type_record(c, &desc);
}
static void check_target(KitArchKind arch, KitOSKind os, KitObjFmt obj) {
KitCompiler* c = new_compiler(arch, os, obj);
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i128_ty = bi.id[KIT_CG_BUILTIN_I128];
- KitCgTypeId f128_ty = bi.id[KIT_CG_BUILTIN_F128];
+ KitCgTypeId i128_ty = builtin(c, KIT_CG_BUILTIN_I128);
+ KitCgTypeId f128_ty = builtin(c, KIT_CG_BUILTIN_F128);
EXPECT(i128_ty != KIT_CG_TYPE_NONE, "%s/%s: missing i128 builtin",
arch_name(arch), os_name(os));
EXPECT(f128_ty != KIT_CG_TYPE_NONE, "%s/%s: missing f128 builtin",
@@ -245,12 +250,12 @@ static void check_target(KitArchKind arch, KitOSKind os, KitObjFmt obj) {
}
if (arch == KIT_ARCH_X86_64) {
- KitCgTypeId f64_i64 =
- record2(c, bi.id[KIT_CG_BUILTIN_F64], bi.id[KIT_CG_BUILTIN_I64]);
- KitCgTypeId i64_f64 =
- record2(c, bi.id[KIT_CG_BUILTIN_I64], bi.id[KIT_CG_BUILTIN_F64]);
- KitCgTypeId f32x2 =
- record2(c, bi.id[KIT_CG_BUILTIN_F32], bi.id[KIT_CG_BUILTIN_F32]);
+ KitCgTypeId f64_i64 = record2(c, builtin(c, KIT_CG_BUILTIN_F64),
+ builtin(c, KIT_CG_BUILTIN_I64));
+ KitCgTypeId i64_f64 = record2(c, builtin(c, KIT_CG_BUILTIN_I64),
+ builtin(c, KIT_CG_BUILTIN_F64));
+ KitCgTypeId f32x2 = record2(c, builtin(c, KIT_CG_BUILTIN_F32),
+ builtin(c, KIT_CG_BUILTIN_F32));
{
const ABIFuncInfo* fi = classify_fn(c, f64_i64, f64_i64);
snprintf(tag, sizeof tag, "%s/%s {double,long} arg", arch_name(arch),
@@ -312,9 +317,9 @@ static void check_target(KitArchKind arch, KitOSKind os, KitObjFmt obj) {
/* Build a record with N i8 fields (so size == N and align == 1). */
static KitCgTypeId make_i8_record(KitCompiler* c, const char* tag_name,
u32 nfields) {
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i8 = bi.id[KIT_CG_BUILTIN_I8];
+ KitCgTypeId i8 = builtin(c, KIT_CG_BUILTIN_I8);
KitCgField fields[16];
+ KitCgRecordDesc desc;
static const char* const names[16] = {"f0", "f1", "f2", "f3", "f4", "f5",
"f6", "f7", "f8", "f9", "fa", "fb",
"fc", "fd", "fe", "ff"};
@@ -324,22 +329,28 @@ static KitCgTypeId make_i8_record(KitCompiler* c, const char* tag_name,
fields[i].name = kit_sym_intern(c, kit_slice_cstr(names[i]));
fields[i].type = i8;
}
- return kit_cg_type_record(c, kit_sym_intern(c, kit_slice_cstr(tag_name)),
- fields, nfields);
+ memset(&desc, 0, sizeof desc);
+ desc.tag = kit_sym_intern(c, kit_slice_cstr(tag_name));
+ desc.fields = fields;
+ desc.nfields = nfields;
+ return kit_cg_type_record(c, &desc);
}
/* Build a record { i64 a; i64 b; } — size 16, align 8. */
static KitCgTypeId make_two_i64_record(KitCompiler* c, const char* tag_n) {
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i64 = bi.id[KIT_CG_BUILTIN_I64];
+ KitCgTypeId i64 = builtin(c, KIT_CG_BUILTIN_I64);
KitCgField fields[2];
+ KitCgRecordDesc desc;
memset(fields, 0, sizeof fields);
fields[0].name = kit_sym_intern(c, KIT_SLICE_LIT("a"));
fields[0].type = i64;
fields[1].name = kit_sym_intern(c, KIT_SLICE_LIT("b"));
fields[1].type = i64;
- return kit_cg_type_record(c, kit_sym_intern(c, kit_slice_cstr(tag_n)), fields,
- 2);
+ memset(&desc, 0, sizeof desc);
+ desc.tag = kit_sym_intern(c, kit_slice_cstr(tag_n));
+ desc.fields = fields;
+ desc.nfields = 2;
+ return kit_cg_type_record(c, &desc);
}
/* Classify a function `ret_ty fn(p0, p1, ..., pN-1)` and return its info. */
@@ -354,11 +365,9 @@ static const ABIFuncInfo* classify_fn_n(KitCompiler* c, KitCgTypeId ret_ty,
memset(params, 0, sizeof params);
for (u32 i = 0; i < nargs; ++i) params[i].type = arg_types[i];
memset(&sig, 0, sizeof sig);
- if (ret_ty != kit_cg_builtin_types(c).id[KIT_CG_BUILTIN_VOID]) {
- memset(&sig_result, 0, sizeof sig_result);
- sig_result.type = ret_ty;
- sig.result = sig_result;
- }
+ memset(&sig_result, 0, sizeof sig_result);
+ sig_result.type = ret_ty;
+ sig.result = sig_result;
sig.params = params;
sig.nparams = nargs;
sig.abi_variadic = variadic ? true : false;
@@ -400,10 +409,10 @@ static void expect_direct_1x_int(const char* tag, const ABIArgInfo* ai,
* placement is codegen, not classifier output). */
static void test_win64_specifics(void) {
KitCompiler* c = new_compiler(KIT_ARCH_X86_64, KIT_OS_WINDOWS, KIT_OBJ_COFF);
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i32 = bi.id[KIT_CG_BUILTIN_I32];
- KitCgTypeId f64 = bi.id[KIT_CG_BUILTIN_F64];
- KitCgTypeId voidp = kit_cg_type_ptr(c, bi.id[KIT_CG_BUILTIN_VOID], 0);
+ KitCgTypeId void_ty = builtin(c, KIT_CG_BUILTIN_VOID);
+ KitCgTypeId i32 = builtin(c, KIT_CG_BUILTIN_I32);
+ KitCgTypeId f64 = builtin(c, KIT_CG_BUILTIN_F64);
+ KitCgTypeId voidp = kit_cg_type_ptr(c, void_ty, 0);
KitCgTypeId rec1 = make_i8_record(c, "S1", 1);
KitCgTypeId rec3 = make_i8_record(c, "S3", 3);
KitCgTypeId rec16 = make_two_i64_record(c, "S16");
@@ -423,8 +432,7 @@ static void test_win64_specifics(void) {
* classifier emits per-arg parts regardless. */
{
KitCgTypeId args[5] = {i32, i32, i32, i32, i32};
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 5, 0);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 5, 0);
EXPECT(fi->nparams == 5, "win64 5xint: nparams=%u want 5",
(unsigned)fi->nparams);
for (u32 i = 0; i < 5; ++i) {
@@ -437,8 +445,7 @@ static void test_win64_specifics(void) {
/* Case 3: void f(double,double,double,double,double) — 5 doubles. */
{
KitCgTypeId args[5] = {f64, f64, f64, f64, f64};
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 5, 0);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 5, 0);
EXPECT(fi->nparams == 5, "win64 5xfp: nparams=%u want 5",
(unsigned)fi->nparams);
for (u32 i = 0; i < 5; ++i) {
@@ -453,8 +460,7 @@ static void test_win64_specifics(void) {
* sharing is a codegen call-site concern. */
{
KitCgTypeId args[4] = {i32, f64, i32, f64};
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 4, 0);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 4, 0);
EXPECT(fi->nparams == 4, "win64 mix: nparams=%u want 4",
(unsigned)fi->nparams);
expect_direct_1x_int("win64 mix arg[0]", &fi->params[0], 4);
@@ -491,8 +497,7 @@ static void test_win64_specifics(void) {
* goes by hidden pointer (BYVAL) on Win64. */
{
KitCgTypeId args[1] = {rec3};
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 1, 0);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 1, 0);
EXPECT(fi->nparams == 1, "win64 S3 arg: nparams=%u want 1",
(unsigned)fi->nparams);
expect_indirect_align("win64 S3 arg", &fi->params[0], /*is_return=*/0,
@@ -527,8 +532,8 @@ static void test_win64_specifics(void) {
* and FP parameters to variadic functions are routed through integer slots. */
static void test_aarch64_windows_variadic(void) {
KitCompiler* c = new_compiler(KIT_ARCH_ARM_64, KIT_OS_WINDOWS, KIT_OBJ_COFF);
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId f64 = bi.id[KIT_CG_BUILTIN_F64];
+ KitCgTypeId void_ty = builtin(c, KIT_CG_BUILTIN_VOID);
+ KitCgTypeId f64 = builtin(c, KIT_CG_BUILTIN_F64);
KitCgTypeId args[1] = {f64};
ABITypeInfo vi = abi_va_list_info(((Compiler*)c)->abi);
@@ -541,14 +546,12 @@ static void test_aarch64_windows_variadic(void) {
(unsigned)vi.scalar_kind, (unsigned)ABI_SC_PTR);
{
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 1, 0);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 1, 0);
expect_direct_1x_fp("aarch64/windows nonvariadic double arg",
&fi->params[0], 8);
}
{
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 1, 1);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 1, 1);
expect_direct_1x_int("aarch64/windows variadic double arg", &fi->params[0],
8);
EXPECT(fi->vararg_on_stack == 0,
@@ -560,11 +563,10 @@ static void test_aarch64_windows_variadic(void) {
static void test_apple_arm64_stack_traits(void) {
KitCompiler* c = new_compiler(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO);
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i32 = bi.id[KIT_CG_BUILTIN_I32];
+ KitCgTypeId void_ty = builtin(c, KIT_CG_BUILTIN_VOID);
+ KitCgTypeId i32 = builtin(c, KIT_CG_BUILTIN_I32);
KitCgTypeId args[1] = {i32};
- const ABIFuncInfo* fi =
- classify_fn_n(c, bi.id[KIT_CG_BUILTIN_VOID], args, 1, 1);
+ const ABIFuncInfo* fi = classify_fn_n(c, void_ty, args, 1, 1);
EXPECT(fi->vararg_on_stack == 1,
"apple arm64 variadic: vararg_on_stack=%u want 1",
@@ -589,22 +591,21 @@ static void check_scalar_split_lane_target(KitArchKind arch, KitOSKind os,
KitObjFmt obj, const char* tag,
u32 want_i64, u32 want_f64) {
KitCompiler* c = new_compiler(arch, os, obj);
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
TargetABI* abi = ((Compiler*)c)->abi;
-
- EXPECT(
- abi_cg_scalar_split_lane_size(abi, bi.id[KIT_CG_BUILTIN_I64]) == want_i64,
- "%s i64 split lane=%u want %u", tag,
- (unsigned)abi_cg_scalar_split_lane_size(abi, bi.id[KIT_CG_BUILTIN_I64]),
- (unsigned)want_i64);
- EXPECT(
- abi_cg_scalar_split_lane_size(abi, bi.id[KIT_CG_BUILTIN_F64]) == want_f64,
- "%s f64 split lane=%u want %u", tag,
- (unsigned)abi_cg_scalar_split_lane_size(abi, bi.id[KIT_CG_BUILTIN_F64]),
- (unsigned)want_f64);
- EXPECT(abi_cg_scalar_split_lane_size(abi, bi.id[KIT_CG_BUILTIN_I32]) == 0,
+ KitCgTypeId i64 = builtin(c, KIT_CG_BUILTIN_I64);
+ KitCgTypeId f64 = builtin(c, KIT_CG_BUILTIN_F64);
+ KitCgTypeId i32 = builtin(c, KIT_CG_BUILTIN_I32);
+ KitCgTypeId f32 = builtin(c, KIT_CG_BUILTIN_F32);
+
+ EXPECT(abi_cg_scalar_split_lane_size(abi, i64) == want_i64,
+ "%s i64 split lane=%u want %u", tag,
+ (unsigned)abi_cg_scalar_split_lane_size(abi, i64), (unsigned)want_i64);
+ EXPECT(abi_cg_scalar_split_lane_size(abi, f64) == want_f64,
+ "%s f64 split lane=%u want %u", tag,
+ (unsigned)abi_cg_scalar_split_lane_size(abi, f64), (unsigned)want_f64);
+ EXPECT(abi_cg_scalar_split_lane_size(abi, i32) == 0,
"%s i32 should not be split-lane", tag);
- EXPECT(abi_cg_scalar_split_lane_size(abi, bi.id[KIT_CG_BUILTIN_F32]) == 0,
+ EXPECT(abi_cg_scalar_split_lane_size(abi, f32) == 0,
"%s f32 should not be split-lane", tag);
kit_compiler_free(c);
diff --git a/test/api/cg_const_test.c b/test/api/cg_const_test.c
@@ -0,0 +1,223 @@
+#include <kit/cg.h>
+#include <kit/core.h>
+#include <kit/object.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "lib/kit_unit.h"
+
+static int make_cg(KitUnit* u, KitCompiler* c, KitCg** cg_out,
+ KitObjBuilder** ob_out) {
+ KitCodeOptions opts;
+ KitObjBuilder* ob = NULL;
+ KitCg* cg = NULL;
+ memset(&opts, 0, sizeof opts);
+ CU_CHECK_RET(u, kit_obj_builder_new(c, &ob) == KIT_OK && ob,
+ "obj builder allocation failed");
+ CU_CHECK_RET(u, kit_cg_new(c, &cg) == KIT_OK && cg, "cg new failed");
+ CU_CHECK_RET(u, kit_cg_begin(cg, ob, &opts) == KIT_OK, "cg begin failed");
+ *cg_out = cg;
+ *ob_out = ob;
+ return 1;
+}
+
+static void destroy_cg(KitCg* cg, KitObjBuilder* ob) {
+ kit_cg_free(cg);
+ kit_obj_builder_free(ob);
+}
+
+static void expect_const(KitUnit* u, KitCg* cg, uint16_t width, uint64_t lo,
+ uint64_t hi, const char* what) {
+ KitCgConstInt v;
+ memset(&v, 0, sizeof v);
+ CU_EXPECT(u, kit_cg_top_const_int_ex(cg, &v), "%s should be known", what);
+ CU_EXPECT(u, v.known && v.width == width && v.lo == lo && v.hi == hi,
+ "%s got known=%u width=%u hi=%llu lo=%llu", what, (unsigned)v.known,
+ (unsigned)v.width, (unsigned long long)v.hi,
+ (unsigned long long)v.lo);
+}
+
+static void test_file_scope_unevaluated(KitUnit* u, KitCompiler* c) {
+ KitCgTypeId i32 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
+ KitCgTypeId i64 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
+ KitCg* cg = NULL;
+ KitObjBuilder* ob = NULL;
+ int64_t i64v = 0;
+ if (!make_cg(u, c, &cg, &ob)) return;
+
+ kit_cg_unevaluated_push(cg);
+ kit_cg_push_int(cg, 40, i64);
+ kit_cg_push_int(cg, 2, i64);
+ kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0);
+ expect_const(u, cg, 64, 42, 0, "file-scope add");
+ CU_EXPECT(u, kit_cg_top_const_i64(cg, &i64v) && i64v == 42,
+ "i64 convenience query should see 42");
+
+ kit_cg_unevaluated_push(cg);
+ kit_cg_push_int(cg, 7, i32);
+ kit_cg_push_int(cg, 3, i32);
+ kit_cg_int_binop(cg, KIT_CG_INT_MUL, 0);
+ expect_const(u, cg, 32, 21, 0, "nested multiply");
+ kit_cg_drop(cg);
+ kit_cg_unevaluated_pop(cg);
+
+ expect_const(u, cg, 64, 42, 0, "outer value after nested pop");
+ kit_cg_drop(cg);
+ kit_cg_unevaluated_pop(cg);
+ CU_EXPECT(u, kit_cg_stack_depth(cg) == 0, "unevaluated stack should empty");
+
+ destroy_cg(cg, ob);
+}
+
+static void test_divrem_and_unknown(KitUnit* u, KitCompiler* c) {
+ KitCgTypeId i64 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
+ KitCg* cg = NULL;
+ KitObjBuilder* ob = NULL;
+ KitCgConstInt v;
+ if (!make_cg(u, c, &cg, &ob)) return;
+ kit_cg_unevaluated_push(cg);
+
+ kit_cg_push_int(cg, 10, i64);
+ kit_cg_push_int(cg, 3, i64);
+ kit_cg_int_binop(cg, KIT_CG_INT_UDIV, 0);
+ expect_const(u, cg, 64, 3, 0, "u64 div");
+ kit_cg_drop(cg);
+
+ kit_cg_push_int(cg, 10, i64);
+ kit_cg_push_int(cg, 3, i64);
+ kit_cg_int_binop(cg, KIT_CG_INT_UREM, 0);
+ expect_const(u, cg, 64, 1, 0, "u64 rem");
+ kit_cg_drop(cg);
+
+ kit_cg_push_int(cg, 10, i64);
+ kit_cg_push_int(cg, 0, i64);
+ kit_cg_int_binop(cg, KIT_CG_INT_UDIV, 0);
+ memset(&v, 0, sizeof v);
+ CU_EXPECT(u, !kit_cg_top_const_int_ex(cg, &v),
+ "divide by zero should produce unknown constant");
+ kit_cg_drop(cg);
+
+ kit_cg_unevaluated_pop(cg);
+ destroy_cg(cg, ob);
+}
+
+static KitCgConstInt u128(uint64_t hi, uint64_t lo) {
+ KitCgConstInt v;
+ memset(&v, 0, sizeof v);
+ v.lo = lo;
+ v.hi = hi;
+ v.width = 128;
+ v.known = 1;
+ return v;
+}
+
+static void test_i128_bit_folds(KitUnit* u, KitCompiler* c) {
+ KitCgTypeId i128 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I128);
+ KitCg* cg = NULL;
+ KitObjBuilder* ob = NULL;
+ KitCgConstInt v;
+ if (!make_cg(u, c, &cg, &ob)) return;
+ kit_cg_unevaluated_push(cg);
+
+ v = u128(0, UINT64_MAX);
+ kit_cg_push_const_int(cg, i128, &v);
+ kit_cg_push_int(cg, 1, i128);
+ kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0);
+ expect_const(u, cg, 128, 0, 1, "i128 add carry");
+ kit_cg_drop(cg);
+
+ v = u128(1, 0);
+ kit_cg_push_const_int(cg, i128, &v);
+ kit_cg_push_int(cg, 2, i128);
+ kit_cg_int_binop(cg, KIT_CG_INT_MUL, 0);
+ expect_const(u, cg, 128, 0, 2, "i128 mul low 128");
+ kit_cg_drop(cg);
+
+ kit_cg_push_int(cg, 1, i128);
+ kit_cg_push_int(cg, 65, i128);
+ kit_cg_int_binop(cg, KIT_CG_INT_SHL, 0);
+ expect_const(u, cg, 128, 0, 2, "i128 shl");
+ kit_cg_drop(cg);
+
+ v = u128(1, 0);
+ kit_cg_push_const_int(cg, i128, &v);
+ v = u128(2, 0);
+ kit_cg_push_const_int(cg, i128, &v);
+ kit_cg_int_cmp(cg, KIT_CG_INT_LT_U);
+ expect_const(u, cg, 32, 1, 0, "i128 cmp");
+ kit_cg_drop(cg);
+
+ v = u128(4, 0);
+ kit_cg_push_const_int(cg, i128, &v);
+ kit_cg_push_int(cg, 2, i128);
+ kit_cg_int_binop(cg, KIT_CG_INT_UDIV, 0);
+ CU_EXPECT(u, !kit_cg_top_const_int_ex(cg, NULL),
+ "i128 div is intentionally unknown");
+ kit_cg_drop(cg);
+
+ kit_cg_unevaluated_pop(cg);
+ destroy_cg(cg, ob);
+}
+
+static void test_dummy_handle_stack_effects(KitUnit* u, KitCompiler* c) {
+ KitCgTypeId i32 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
+ KitCgTypeId fn;
+ KitCgFuncResult result;
+ KitCgFuncSig sig;
+ KitCgDecl decl;
+ KitCgSym sym;
+ KitCg* cg = NULL;
+ KitObjBuilder* ob = NULL;
+ if (!make_cg(u, c, &cg, &ob)) return;
+
+ memset(&result, 0, sizeof result);
+ result.type = i32;
+ memset(&sig, 0, sizeof sig);
+ sig.result = result;
+ sig.call_conv = KIT_CG_CC_TARGET_C;
+ fn = kit_cg_type_func(c, sig);
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_FUNC;
+ decl.linkage_name = kit_sym_intern(c, KIT_SLICE_LIT("callee"));
+ decl.display_name = decl.linkage_name;
+ decl.type = fn;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.sym.visibility = KIT_CG_VIS_DEFAULT;
+ sym = kit_cg_decl(cg, decl);
+
+ kit_cg_unevaluated_push(cg);
+ kit_cg_push_label_addr(cg, kit_cg_label_new(cg), kit_cg_type_ptr(c, i32, 0));
+ CU_EXPECT(u, kit_cg_stack_depth(cg) == 1, "label addr should push one");
+ CU_EXPECT(u, !kit_cg_top_const_int_ex(cg, NULL),
+ "label address should not be an integer constant");
+ kit_cg_drop(cg);
+
+ kit_cg_push_symbol_addr(cg, sym, 0);
+ kit_cg_call(cg, 0, fn, (KitCgCallAttrs){0});
+ CU_EXPECT(u, kit_cg_stack_depth(cg) == 1, "call should push result");
+ CU_EXPECT(u, !kit_cg_top_const_int_ex(cg, NULL),
+ "call result should be unknown");
+ kit_cg_drop(cg);
+ kit_cg_unevaluated_pop(cg);
+
+ destroy_cg(cg, ob);
+}
+
+int main(void) {
+ KitUnit u;
+ KitCompiler* c = NULL;
+ KitTargetSpec t;
+ kit_unit_init(&u);
+ t = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF);
+ CU_EXPECT(&u, kit_unit_compiler_new(&u, t, &c) == KIT_OK && c,
+ "compiler allocation failed");
+ if (c) {
+ test_file_scope_unevaluated(&u, c);
+ test_divrem_and_unknown(&u, c);
+ test_i128_bit_folds(&u, c);
+ test_dummy_handle_stack_effects(&u, c);
+ kit_compiler_free(c);
+ }
+ kit_unit_summary(&u, "cg_const_test");
+ return kit_unit_status(&u);
+}
diff --git a/test/api/cg_control_test.c b/test/api/cg_control_test.c
@@ -72,8 +72,7 @@ static int64_t loop_expected(int64_t n) {
* `continue` (in the break_* shapes). */
static void build_loop_fn(KitCompiler* c, KitCg* cg, const char* name,
Variant variant) {
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i32 = bi.id[KIT_CG_BUILTIN_I32];
+ KitCgTypeId i32 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
KitCgFuncParam param;
KitCgFuncResult result;
KitCgFuncSig sig;
@@ -120,7 +119,7 @@ static void build_loop_fn(KitCompiler* c, KitCg* cg, const char* name,
kit_cg_push_int(cg, 0, i32);
kit_cg_local_write(cg, acc_local, ma);
- scope = kit_cg_scope_begin(cg, KIT_CG_TYPE_NONE);
+ scope = kit_cg_scope_begin(cg);
if (variant == V_BREAK_TRUE || variant == V_BREAK_FALSE) {
/* test at top: break out when i has reached n */
@@ -177,11 +176,12 @@ static void build_loop_fn(KitCompiler* c, KitCg* cg, const char* name,
* so the same interpreter/emit harness as the conditional variants applies. */
/* Boilerplate: declare `int <name>(int)` and open it; returns the param
- * local and fills *out_i32 with the i32 type id and *out_ma with an i32 access. */
+ * local and fills *out_i32 with the i32 type id and *out_ma with an i32 access.
+ */
static KitCgLocal begin_int_int_fn(KitCompiler* c, KitCg* cg, const char* name,
- KitCgTypeId* out_i32, KitCgMemAccess* out_ma) {
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i32 = bi.id[KIT_CG_BUILTIN_I32];
+ KitCgTypeId* out_i32,
+ KitCgMemAccess* out_ma) {
+ KitCgTypeId i32 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
KitCgFuncParam param;
KitCgFuncResult result;
KitCgFuncSig sig;
@@ -217,7 +217,8 @@ static KitCgLocal begin_int_int_fn(KitCompiler* c, KitCg* cg, const char* name,
/* int two_result_block(int x) { return (x+1) + (x+2); } // == 2x + 3
* via a block carrying two i32 results that scope_end yields bottom->top. */
-static void build_two_result_block(KitCompiler* c, KitCg* cg, const char* name) {
+static void build_two_result_block(KitCompiler* c, KitCg* cg,
+ const char* name) {
KitCgTypeId i32;
KitCgMemAccess ma;
KitCgLocal x = begin_int_int_fn(c, cg, name, &i32, &ma);
@@ -240,10 +241,10 @@ static void build_two_result_block(KitCompiler* c, KitCg* cg, const char* name)
kit_cg_func_end(cg);
}
-/* int one_param_loop(int n) { int acc=0; for (int i=0;i<n;i++) acc+=i; return acc; }
- * The loop carries i as a single param across the back edge; acc is a plain
- * local. Exercises scope_begin_sig(1 param) + store_params + continue, with the
- * loop exited by falling through the body bottom (no break). */
+/* int one_param_loop(int n) { int acc=0; for (int i=0;i<n;i++) acc+=i; return
+ * acc; } The loop carries i as a single param across the back edge; acc is a
+ * plain local. Exercises scope_begin_sig(1 param) + store_params + continue,
+ * with the loop exited by falling through the body bottom (no break). */
static void build_one_param_loop(KitCompiler* c, KitCg* cg, const char* name) {
KitCgTypeId i32;
KitCgMemAccess ma;
@@ -333,9 +334,9 @@ static void build_two_param_loop(KitCompiler* c, KitCg* cg, const char* name) {
kit_cg_local_read(cg, i_l, ma);
kit_cg_push_int(cg, 1, i32);
kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0);
- kit_cg_local_write(cg, i_l, ma); /* i += 1 */
- kit_cg_local_read(cg, i_l, ma); /* fresh param i */
- kit_cg_local_read(cg, acc_l, ma); /* fresh param acc */
+ kit_cg_local_write(cg, i_l, ma); /* i += 1 */
+ kit_cg_local_read(cg, i_l, ma); /* fresh param i */
+ kit_cg_local_read(cg, acc_l, ma); /* fresh param acc */
kit_cg_scope_store_params(cg, loop); /* reverse-pop into [i,acc] locals */
kit_cg_jump(cg, kit_cg_scope_continue_label(cg, loop));
kit_cg_label_place(cg, done);
@@ -486,13 +487,15 @@ typedef struct {
static KitStatus reject_body(KitCompiler* c, void* user) {
RejectCtx* r = (RejectCtx*)user;
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
KitCgFuncSig sig;
+ KitCgFuncResult result;
KitCgDecl decl;
KitCgSym sym;
KitCgScope blk;
- (void)bi;
memset(&sig, 0, sizeof sig);
+ memset(&result, 0, sizeof result);
+ result.type = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
+ sig.result = result;
sig.call_conv = KIT_CG_CC_TARGET_C; /* void() */
memset(&decl, 0, sizeof decl);
decl.kind = KIT_CG_DECL_FUNC;
@@ -503,7 +506,7 @@ static KitStatus reject_body(KitCompiler* c, void* user) {
decl.sym.visibility = KIT_CG_VIS_DEFAULT;
sym = kit_cg_decl(r->cg, decl);
kit_cg_func_begin(r->cg, sym);
- blk = kit_cg_block_begin(r->cg, KIT_CG_TYPE_NONE);
+ blk = kit_cg_block_begin(r->cg);
kit_cg_continue(r->cg, blk); /* <- must panic: blocks have no loop header */
kit_cg_scope_end(r->cg, blk);
kit_cg_ret(r->cg);
diff --git a/test/api/cg_fp_cmp_test.c b/test/api/cg_fp_cmp_test.c
@@ -109,10 +109,9 @@ static double bits_to_double(uint64_t b) {
* declared symbol; the function is captured by `name` for interp lookup. */
static void build_cmp_fn(KitCompiler* c, KitCg* cg, const char* name,
KitCgFpCmpOp op, int use_f128) {
- KitCgBuiltinTypes bi = kit_cg_builtin_types(c);
- KitCgTypeId i32 = bi.id[KIT_CG_BUILTIN_I32];
- KitCgTypeId f64 = bi.id[KIT_CG_BUILTIN_F64];
- KitCgTypeId f128 = bi.id[KIT_CG_BUILTIN_F128];
+ KitCgTypeId i32 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
+ KitCgTypeId f64 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64);
+ KitCgTypeId f128 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_F128);
KitCgFuncParam params[2];
KitCgFuncResult result;
KitCgFuncSig sig;
diff --git a/test/api/cg_switch_test.c b/test/api/cg_switch_test.c
@@ -17,6 +17,8 @@
#include <kit/cg.h>
#include <kit/core.h>
+#include <kit/disasm.h>
+#include <kit/interp.h>
#include <kit/object.h>
#include <stdarg.h>
#include <stdio.h>
@@ -247,12 +249,325 @@ static void run_all_shapes(KitCompiler* c, KitCgTypeId i32_ty,
}
}
+/* ---- Jump-table decision -------------------------------------------- *
+ *
+ * The shape checks above only assert that lowering succeeds. These build a
+ * function, emit an ELF object for the test compiler's (aa64) target,
+ * disassemble .text, and report whether the dispatch became a real jump
+ * table — detectable as the AArch64 indirect register branch `br`, which
+ * the cmp/branch chain never emits. This is what lets us assert the
+ * *policy* decision (table vs chain), not just structural success. */
+
+/* Build `int32_t <name>(<sel> x){ switch(x){...} }` from `sh` into an
+ * already-begun cg, shared by the disasm and interpreter checks. Returns 0
+ * on success, -1 on a harness failure. */
+static int emit_switch_fn(KitCompiler* c, KitCg* cg, const char* name,
+ KitCgTypeId i32_ty, const SwitchShape* sh) {
+ KitCgFuncParam param_desc;
+ KitCgFuncResult sig_result;
+ KitCgFuncSig sig;
+ KitCgDecl decl;
+ KitCgSym sym;
+ KitCgLocalAttrs attrs;
+ KitCgLocal param;
+ KitCgLabel default_lbl;
+ KitCgLabel end_lbl;
+ KitCgLabel* case_lbls = NULL;
+ KitCgSwitchCase* cases = NULL;
+ KitCgSwitch sw;
+ int rc = -1;
+ uint32_t i;
+
+ memset(¶m_desc, 0, sizeof param_desc);
+ param_desc.type = sh->selector_type;
+ memset(&sig_result, 0, sizeof sig_result);
+ sig_result.type = i32_ty;
+ memset(&sig, 0, sizeof sig);
+ sig.result = sig_result;
+ sig.params = ¶m_desc;
+ sig.nparams = 1;
+ sig.call_conv = KIT_CG_CC_TARGET_C;
+
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_FUNC;
+ decl.linkage_name = kit_sym_intern(c, kit_slice_cstr(name));
+ decl.display_name = decl.linkage_name;
+ decl.type = kit_cg_type_func(c, sig);
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.sym.visibility = KIT_CG_VIS_DEFAULT;
+ sym = kit_cg_decl(cg, decl);
+ if (sym == KIT_CG_SYM_NONE) return -1;
+
+ kit_cg_func_begin(cg, sym);
+ memset(&attrs, 0, sizeof attrs);
+ attrs.name = kit_sym_intern(c, KIT_SLICE_LIT("x"));
+ param = kit_cg_param(cg, 0, sh->selector_type, attrs);
+ if (param == KIT_CG_LOCAL_NONE) return -1;
+
+ end_lbl = kit_cg_label_new(cg);
+ default_lbl = sh->has_default ? kit_cg_label_new(cg) : end_lbl;
+ if (sh->ncases) {
+ case_lbls = (KitCgLabel*)malloc(sh->ncases * sizeof *case_lbls);
+ cases = (KitCgSwitchCase*)malloc(sh->ncases * sizeof *cases);
+ if (!case_lbls || !cases) goto done;
+ for (i = 0; i < sh->ncases; ++i) {
+ case_lbls[i] = kit_cg_label_new(cg);
+ cases[i].value = (uint64_t)sh->values[i];
+ cases[i].label = case_lbls[i];
+ }
+ }
+
+ kit_cg_push_local(cg, param);
+ kit_cg_load(
+ cg, (KitCgMemAccess){.type = sh->selector_type,
+ .align = kit_cg_type_align(c, sh->selector_type)});
+ memset(&sw, 0, sizeof sw);
+ sw.selector_type = sh->selector_type;
+ sw.default_label = default_lbl;
+ sw.cases = cases;
+ sw.ncases = sh->ncases;
+ sw.hint = sh->hint;
+ kit_cg_switch(cg, sw);
+
+ for (i = 0; i < sh->ncases; ++i) {
+ kit_cg_label_place(cg, case_lbls[i]);
+ kit_cg_push_int(cg, (uint64_t)(int64_t)sh->results[i], i32_ty);
+ kit_cg_ret(cg);
+ }
+ if (sh->has_default) {
+ kit_cg_label_place(cg, default_lbl);
+ kit_cg_push_int(cg, (uint64_t)(int64_t)sh->default_result, i32_ty);
+ kit_cg_ret(cg);
+ }
+ kit_cg_label_place(cg, end_lbl);
+ kit_cg_push_int(cg, (uint64_t)(int64_t)-1, i32_ty);
+ kit_cg_ret(cg);
+ kit_cg_func_end(cg);
+ rc = 0;
+
+done:
+ free(case_lbls);
+ free(cases);
+ return rc;
+}
+
+/* 1 iff .text contains an aa64 indirect register branch (BR). -1 on harness
+ * failure. */
+static int switch_lowers_to_table(KitCompiler* c, KitCgTypeId i32_ty,
+ const SwitchShape* sh, int opt_level) {
+ KitCodeOptions opts;
+ KitObjBuilder* ob;
+ KitCg* cg = NULL;
+ KitWriter* writer = NULL;
+ KitObjFile* file = NULL;
+ KitSlice bytes;
+ KitObjSection text_sec;
+ const uint8_t* data = NULL;
+ size_t len = 0;
+ KitDisasmContext dc;
+ KitDisasmIter* it = NULL;
+ KitInsn insn;
+ int found = -1;
+
+ memset(&opts, 0, sizeof opts);
+ opts.opt_level = opt_level;
+ ob = new_obj(c);
+ if (!ob) return -1;
+ if (kit_cg_new(c, &cg) != KIT_OK || !cg) {
+ kit_obj_builder_free(ob);
+ return -1;
+ }
+ if (kit_cg_begin(cg, ob, &opts) != KIT_OK) goto done;
+ if (emit_switch_fn(c, cg, "f", i32_ty, sh) != 0) goto done;
+
+ if (kit_cg_finish(cg, NULL) != KIT_OK) goto done;
+ if (kit_cg_detach(cg) != KIT_OK) goto done;
+
+ if (kit_writer_mem(&g_u.heap, &writer) != KIT_OK || !writer) goto done;
+ if (kit_obj_builder_emit(ob, writer) != KIT_OK) goto done;
+ bytes.data = kit_writer_mem_bytes(writer, &len);
+ bytes.len = len;
+ if (kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<sw-test>"), &bytes, &file) !=
+ KIT_OK)
+ goto done;
+ if (kit_obj_section_by_name(file, KIT_SLICE_LIT(".text"), &text_sec) !=
+ KIT_OK)
+ goto done;
+ if (kit_obj_section_data(file, text_sec, &data, &len) != KIT_OK) goto done;
+
+ memset(&dc, 0, sizeof dc);
+ dc.target = kit_compiler_target(c);
+ dc.context = g_u.ctx;
+ if (kit_disasm_iter_new(&dc, data, len, 0, file, &it) != KIT_OK || !it)
+ goto done;
+ found = 0;
+ while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) {
+ if (insn.mnemonic.len == 2 && insn.mnemonic.s &&
+ insn.mnemonic.s[0] == 'b' && insn.mnemonic.s[1] == 'r') {
+ found = 1;
+ break;
+ }
+ }
+
+done:
+ if (it) kit_disasm_iter_free(it);
+ if (file) kit_obj_free(file);
+ if (writer) kit_writer_close(writer);
+ if (cg) kit_cg_free(cg);
+ kit_obj_builder_free(ob);
+ return found;
+}
+
+static void run_table_decision_checks(KitCompiler* c, KitCgTypeId i32_ty,
+ KitCgTypeId i64_ty) {
+ /* Six contiguous 64-bit values straddling the signed midpoint 2^63.
+ * As *unsigned* they span just [2^63-3, 2^63+2] (span 6, table-friendly),
+ * but a signed-only reading sees them at both ends of the i64 range and
+ * the table is rejected. The selector signedness isn't even visible to
+ * the lowering (CG has only I64), so the planner must take the tighter of
+ * the signed- and unsigned-seam windows. */
+ static const uint64_t MID = (uint64_t)1 << 63;
+ static int64_t mid_vals[6];
+ static const int32_t mid_res[] = {200, 201, 202, 203, 204, 205};
+ /* Dense small positive values: a table under either reading — the
+ * positive control that the detector reports tables when one is emitted. */
+ static const int64_t lo_vals[] = {10, 11, 12, 13, 14, 15};
+ static const int32_t lo_res[] = {100, 101, 102, 103, 104, 105};
+ /* Genuinely sparse: span far exceeds MAX_SPAN under either window, so no
+ * table even with the JUMP_TABLE hint — the negative control. */
+ static const int64_t sparse_vals[] = {0, 1ll << 20, 1ll << 40, 1ll << 60};
+ static const int32_t sparse_res[] = {1, 2, 3, 4};
+ SwitchShape mid;
+ SwitchShape lo;
+ SwitchShape sparse;
+ int r;
+ uint32_t i;
+
+ for (i = 0; i < 6; ++i) mid_vals[i] = (int64_t)(MID - 3 + i);
+
+ memset(&mid, 0, sizeof mid);
+ mid.name = "u64_midpoint";
+ mid.selector_type = i64_ty;
+ mid.values = mid_vals;
+ mid.results = mid_res;
+ mid.ncases = 6;
+ mid.has_default = 1;
+ mid.default_result = 999;
+ mid.hint = KIT_CG_SWITCH_JUMP_TABLE;
+
+ lo = mid;
+ lo.name = "dense_low";
+ lo.values = lo_vals;
+ lo.results = lo_res;
+
+ sparse = mid;
+ sparse.name = "sparse";
+ sparse.values = sparse_vals;
+ sparse.results = sparse_res;
+ sparse.ncases = 4;
+
+ /* Positive control: a dense low switch must produce a table. */
+ r = switch_lowers_to_table(c, i32_ty, &lo, /*opt_level=*/0);
+ EXPECT(r == 1, "dense low switch should lower to a jump table (got %d)", r);
+
+ /* The fix: midpoint-straddling 64-bit cases are dense under the unsigned
+ * window and must lower to a table rather than a cmp/branch chain. */
+ r = switch_lowers_to_table(c, i32_ty, &mid, /*opt_level=*/0);
+ EXPECT(r == 1,
+ "u64 midpoint-straddling switch should lower to a jump table (got %d)",
+ r);
+
+ /* Negative control: a truly sparse switch stays a chain even when the
+ * frontend forces the jump-table hint. */
+ r = switch_lowers_to_table(c, i32_ty, &sparse, /*opt_level=*/0);
+ EXPECT(r == 0, "sparse switch must not lower to a jump table (got %d)", r);
+}
+
+/* Execute the midpoint switch through the target-independent interpreter.
+ * At O1 the JUMP_TABLE plan reaches cg_emit_switch_table, which records the
+ * `idx = sel - vmin` / unsigned-bounds / table-load / indirect-branch IR; the
+ * interp materializes the table as interp pcs and runs those very ops, so this
+ * exercises the new unsigned-window index math end to end (not just that a
+ * table was chosen). Every in-set selector must reach its arm and every
+ * out-of-set selector must reach default. */
+static void run_table_exec_check(KitCompiler* c, KitCgTypeId i32_ty,
+ KitCgTypeId i64_ty) {
+ static const uint64_t MID = (uint64_t)1 << 63;
+ static int64_t mid_vals[6];
+ static const int32_t mid_res[] = {200, 201, 202, 203, 204, 205};
+ SwitchShape mid;
+ KitInterpProgram* pp;
+ KitObjBuilder* ob = NULL;
+ KitCg* cg = NULL;
+ KitCodeOptions opts;
+ KitInterpFunc* fn;
+ uint32_t i;
+
+ for (i = 0; i < 6; ++i) mid_vals[i] = (int64_t)(MID - 3 + i);
+ memset(&mid, 0, sizeof mid);
+ mid.name = "u64_midpoint_exec";
+ mid.selector_type = i64_ty;
+ mid.values = mid_vals;
+ mid.results = mid_res;
+ mid.ncases = 6;
+ mid.has_default = 1;
+ mid.default_result = 999;
+ mid.hint = KIT_CG_SWITCH_JUMP_TABLE;
+
+ pp = kit_interp_program_new(c);
+ EXPECT(pp != NULL, "exec: interp_program_new failed");
+ if (!pp) return;
+ kit_interp_program_attach(pp, c);
+
+ ob = new_obj(c);
+ EXPECT(ob != NULL, "exec: obj_new");
+ EXPECT(kit_cg_new(c, &cg) == KIT_OK && cg, "exec: cg_new");
+ if (ob && cg) {
+ memset(&opts, 0, sizeof opts);
+ opts.opt_level = 1; /* interp capture requires the optimizer pass */
+ kit_cg_begin(cg, ob, &opts);
+ EXPECT(emit_switch_fn(c, cg, "mid", i32_ty, &mid) == 0, "exec: build");
+ EXPECT(kit_cg_finish(cg, NULL) == KIT_OK, "exec: finish");
+ EXPECT(kit_cg_detach(cg) == KIT_OK, "exec: detach");
+
+ fn = kit_interp_lookup(pp, kit_slice_cstr("mid"));
+ EXPECT(fn != NULL, "exec: mid not captured");
+ if (fn) {
+ for (i = 0; i < 6; ++i) {
+ uint64_t args[1] = {(uint64_t)mid_vals[i]};
+ int64_t ret = 0;
+ KitInterpStatus s = kit_interp_call_args(pp, fn, args, 1, &ret);
+ EXPECT(s == KIT_INTERP_DONE && ret == mid_res[i],
+ "mid(%#llx): want %d got %lld (status %d)",
+ (unsigned long long)args[0], mid_res[i], (long long)ret, (int)s);
+ }
+ /* Out-of-set selectors stress both bounds edges (just below vmin, just
+ * above vmax) and a far-away value — all must reach default. */
+ {
+ uint64_t outs[] = {MID - 4, MID + 3, 0, MID + 1000};
+ uint32_t no = (uint32_t)(sizeof outs / sizeof outs[0]);
+ for (i = 0; i < no; ++i) {
+ uint64_t args[1] = {outs[i]};
+ int64_t ret = 0;
+ KitInterpStatus s = kit_interp_call_args(pp, fn, args, 1, &ret);
+ EXPECT(s == KIT_INTERP_DONE && ret == 999,
+ "mid(%#llx): want default 999 got %lld (status %d)",
+ (unsigned long long)outs[i], (long long)ret, (int)s);
+ }
+ }
+ }
+ }
+
+ if (cg) kit_cg_free(cg);
+ if (ob) kit_obj_builder_free(ob);
+ kit_interp_program_free(pp);
+}
+
/* ---- Entry ----------------------------------------------------------- */
int main(void) {
KitTargetSpec target;
KitCompiler* c = NULL;
- KitCgBuiltinTypes bi;
KitCgTypeId i32_ty;
KitCgTypeId i64_ty;
@@ -264,14 +579,15 @@ int main(void) {
return 2;
}
- bi = kit_cg_builtin_types(c);
- i32_ty = bi.id[KIT_CG_BUILTIN_I32];
- i64_ty = bi.id[KIT_CG_BUILTIN_I64];
+ i32_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
+ i64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
EXPECT(i32_ty != KIT_CG_TYPE_NONE, "i32 builtin id is none");
EXPECT(i64_ty != KIT_CG_TYPE_NONE, "i64 builtin id is none");
run_all_shapes(c, i32_ty, i64_ty, /*opt_level=*/0);
run_all_shapes(c, i32_ty, i64_ty, /*opt_level=*/1);
+ run_table_decision_checks(c, i32_ty, i64_ty);
+ run_table_exec_check(c, i32_ty, i64_ty);
kit_compiler_free(c);
kit_unit_summary(&g_u, "cg_switch_test");
diff --git a/test/api/cg_type_test.c b/test/api/cg_type_test.c
@@ -1342,6 +1342,158 @@ static void exercise_cg_memop_constfold_shape(KitCompiler* c,
kit_writer_close(dump);
}
+static KitCg* begin_void_cg_func(KitCompiler* c, const char* name,
+ KitObjBuilder** ob_out) {
+ KitCodeOptions opts;
+ KitObjBuilder* ob;
+ KitCg* cg;
+ KitCgFuncSig sig;
+ KitCgFuncResult result;
+ KitCgDecl decl;
+ KitCgSym sym;
+
+ if (ob_out) *ob_out = NULL;
+ memset(&opts, 0, sizeof opts);
+ ob = new_obj(c);
+ EXPECT(ob != NULL, "%s obj builder allocation failed", name);
+ if (!ob) return NULL;
+ cg = NULL;
+ EXPECT(kit_cg_new(c, &cg) == KIT_OK && cg != NULL, "%s cg allocation failed",
+ name);
+ if (!cg) {
+ kit_obj_builder_free(ob);
+ return NULL;
+ }
+ EXPECT(kit_cg_begin(cg, ob, &opts) == KIT_OK, "%s cg begin failed", name);
+
+ memset(&sig, 0, sizeof sig);
+ memset(&result, 0, sizeof result);
+ result.type = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
+ sig.result = result;
+ sig.call_conv = KIT_CG_CC_TARGET_C;
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_FUNC;
+ decl.linkage_name = kit_sym_intern(c, kit_slice_cstr(name));
+ decl.display_name = decl.linkage_name;
+ decl.type = kit_cg_type_func(c, sig);
+ decl.sym.bind = KIT_SB_LOCAL;
+ decl.sym.visibility = KIT_CG_VIS_DEFAULT;
+ sym = kit_cg_decl(cg, decl);
+ EXPECT(sym != KIT_CG_SYM_NONE, "%s decl failed", name);
+ if (sym == KIT_CG_SYM_NONE) {
+ kit_cg_free(cg);
+ kit_obj_builder_free(ob);
+ return NULL;
+ }
+ kit_cg_func_begin(cg, sym);
+ if (ob_out) *ob_out = ob;
+ return cg;
+}
+
+static void exercise_cg_slot_lang_facts(KitCompiler* c, KitCgTypeId i32_ty) {
+ static const char tag_a[] = "slot-a";
+ static const char tag_b[] = "slot-b";
+ KitObjBuilder* ob;
+ KitCg* cg = begin_void_cg_func(c, "cg_slot_lang_facts", &ob);
+ KitCgSlotInfo info;
+ KitCgScope scope;
+ if (!cg) return;
+
+ kit_cg_push_int(cg, 1, i32_ty);
+ kit_cg_retag_top(cg, tag_a, 0x0003u);
+ kit_cg_dup(cg);
+ info = kit_cg_slot_info(cg, 0);
+ EXPECT(info.cg_type == i32_ty && info.lang_type == tag_a &&
+ info.lang_flags == 0x0003u,
+ "dup should copy top slot facts");
+ info = kit_cg_slot_info(cg, 1);
+ EXPECT(info.lang_type == tag_a && info.lang_flags == 0x0003u,
+ "dup should preserve original slot facts");
+ kit_cg_retag_at(cg, 1, tag_b, 0x0010u);
+ kit_cg_set_top_flags(cg, 0x0004u, 0x0001u);
+ info = kit_cg_slot_info(cg, 0);
+ EXPECT(info.lang_type == tag_a && info.lang_flags == 0x0006u,
+ "set_top_flags should update only top flags");
+ info = kit_cg_slot_info(cg, 1);
+ EXPECT(info.lang_type == tag_b && info.lang_flags == 0x0010u,
+ "retag_at should update depth-selected slot");
+ kit_cg_drop(cg);
+ kit_cg_drop(cg);
+
+ kit_cg_push_int(cg, 2, i32_ty);
+ kit_cg_retag_top(cg, tag_a, 0x00ffu);
+ kit_cg_push_int(cg, 3, i32_ty);
+ kit_cg_retag_top(cg, tag_b, 0x00eeu);
+ kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0);
+ info = kit_cg_slot_info(cg, 0);
+ EXPECT(
+ info.cg_type == i32_ty && info.lang_type == NULL && info.lang_flags == 0,
+ "fresh binop producer should clear slot facts");
+ kit_cg_drop(cg);
+
+ scope = kit_cg_block_begin_value(cg, i32_ty);
+ kit_cg_push_int(cg, 4, i32_ty);
+ kit_cg_retag_top(cg, tag_b, 0x0033u);
+ kit_cg_scope_end(cg, scope);
+ info = kit_cg_slot_info(cg, 0);
+ EXPECT(info.cg_type == i32_ty && info.lang_type == tag_b &&
+ info.lang_flags == 0x0033u,
+ "scope result mover should preserve slot facts");
+ kit_cg_drop(cg);
+
+ kit_cg_ret(cg);
+ kit_cg_func_end(cg);
+ kit_cg_free(cg);
+ kit_obj_builder_free(ob);
+}
+
+static void exercise_cg_field_at_store_keep(KitCompiler* c, KitCgTypeId i32_ty,
+ KitCgTypeId rec_ty) {
+ static const char value_tag[] = "assigned-value";
+ KitObjBuilder* ob;
+ KitCg* cg = begin_void_cg_func(c, "cg_field_at_store_keep", &ob);
+ KitCgLocalAttrs attrs;
+ KitCgLocal local;
+ KitCgMemAccess mem;
+ KitCgSlotInfo info;
+ if (!cg) return;
+
+ memset(&attrs, 0, sizeof attrs);
+ attrs.name = kit_sym_intern(c, KIT_SLICE_LIT("r"));
+ local = kit_cg_local(cg, rec_ty, attrs);
+ EXPECT(local != KIT_CG_LOCAL_NONE, "field_at local allocation failed");
+
+ memset(&mem, 0, sizeof mem);
+ mem.type = i32_ty;
+ mem.align = kit_cg_type_align(c, i32_ty);
+
+ kit_cg_push_local(cg, local);
+ kit_cg_field_at(cg, 4, i32_ty);
+ kit_cg_push_int(cg, 42, i32_ty);
+ kit_cg_retag_top(cg, value_tag, 0x0042u);
+ kit_cg_store_keep(cg, mem);
+ EXPECT(kit_cg_stack_depth(cg) == 1u, "store_keep should leave one value");
+ info = kit_cg_slot_info(cg, 0);
+ EXPECT(info.cg_type == i32_ty && info.lang_type == value_tag &&
+ info.lang_flags == 0x0042u,
+ "store_keep should preserve assigned value slot facts");
+ kit_cg_drop(cg);
+
+ kit_cg_push_local(cg, local);
+ kit_cg_field_at(cg, 4, i32_ty);
+ kit_cg_load(cg, mem);
+ info = kit_cg_slot_info(cg, 0);
+ EXPECT(
+ info.cg_type == i32_ty && info.lang_type == NULL && info.lang_flags == 0,
+ "load through field_at should produce a freshly untagged value");
+ kit_cg_drop(cg);
+
+ kit_cg_ret(cg);
+ kit_cg_func_end(cg);
+ kit_cg_free(cg);
+ kit_obj_builder_free(ob);
+}
+
typedef struct BadStoreCtx {
KitCompiler* c;
KitCgTypeId i32_ty;
@@ -1354,6 +1506,7 @@ static KitCg* cg_begin_bad_store_func(KitCompiler* c, const char* name) {
KitObjBuilder* ob;
KitCg* cg;
KitCgFuncSig sig;
+ KitCgFuncResult result;
KitCgDecl decl;
KitCgSym sym;
@@ -1371,6 +1524,9 @@ static KitCg* cg_begin_bad_store_func(KitCompiler* c, const char* name) {
}
memset(&sig, 0, sizeof sig);
+ memset(&result, 0, sizeof result);
+ result.type = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
+ sig.result = result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
decl.kind = KIT_CG_DECL_FUNC;
@@ -1381,6 +1537,11 @@ static KitCg* cg_begin_bad_store_func(KitCompiler* c, const char* name) {
decl.sym.visibility = KIT_CG_VIS_DEFAULT;
sym = kit_cg_decl(cg, decl);
EXPECT(sym != KIT_CG_SYM_NONE, "bad-store function decl failed");
+ if (sym == KIT_CG_SYM_NONE) {
+ kit_cg_free(cg);
+ kit_obj_builder_free(ob);
+ return NULL;
+ }
kit_cg_func_begin(cg, sym);
return cg;
}
@@ -1562,7 +1723,6 @@ static void exercise_cg_free_does_not_finish(KitCompiler* c,
int main(void) {
KitTargetSpec target;
KitCompiler* c;
- KitCgBuiltinTypes bi;
KitCgTypeId void_ty;
KitCgTypeId i8_ty;
KitCgTypeId i32_ty;
@@ -1571,12 +1731,21 @@ int main(void) {
KitCgTypeId va_list_ty;
KitCgTypeId ptr_i32;
KitCgTypeId array_i32;
+ KitCgTypeId huge_array_a;
+ KitCgTypeId huge_array_b;
KitCgFuncParam params[2];
KitCgFuncSig sig;
KitCgTypeId fn;
KitCgTypeId alias;
+ KitCgTypeId alias_ptr_i32;
+ KitCgTypeId ptr_alias_i32;
+ KitCgTypeInfo info;
KitCgTypeId rec;
KitCgTypeId rec_ex;
+ KitCgTypeId rec_decl;
+ KitCgTypeId rec_ptr;
+ KitCgTypeId rec_self_ptr;
+ KitCgTypeId rec_bad;
KitCgTypeId enm;
KitCgRecordDesc rdesc;
KitCgField fields[2];
@@ -1593,19 +1762,25 @@ int main(void) {
return 2;
}
- bi = kit_cg_builtin_types(c);
- void_ty = bi.id[KIT_CG_BUILTIN_VOID];
- i8_ty = bi.id[KIT_CG_BUILTIN_I8];
- i32_ty = bi.id[KIT_CG_BUILTIN_I32];
- i64_ty = bi.id[KIT_CG_BUILTIN_I64];
- f64_ty = bi.id[KIT_CG_BUILTIN_F64];
- va_list_ty = bi.id[KIT_CG_BUILTIN_VARARG_STATE];
+ void_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
+ i8_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I8);
+ i32_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
+ i64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
+ f64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64);
+ va_list_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VARARG_STATE);
EXPECT(void_ty != KIT_CG_TYPE_NONE, "void builtin id is none");
EXPECT(i8_ty != KIT_CG_TYPE_NONE, "i8 builtin id is none");
EXPECT(i32_ty != KIT_CG_TYPE_NONE, "i32 builtin id is none");
EXPECT(f64_ty != KIT_CG_TYPE_NONE, "f64 builtin id is none");
EXPECT(va_list_ty != KIT_CG_TYPE_NONE, "va_list builtin id is none");
EXPECT(void_ty != i32_ty && i32_ty != f64_ty, "builtin ids collide");
+ EXPECT(void_ty == 1u, "void builtin should be first direct id");
+ EXPECT(i8_ty == (KitCgTypeId)KIT_CG_BUILTIN_I8 + 1u,
+ "builtin id should be direct enum+1");
+ EXPECT(!kit_cg_type_is_void(c, KIT_CG_TYPE_NONE),
+ "invalid type id must not masquerade as void");
+ EXPECT(kit_cg_type_is_void(c, void_ty), "void builtin predicate failed");
+ EXPECT(!kit_cg_type_is_sized(c, void_ty), "void should not be sized");
ptr_i32 = kit_cg_type_ptr(c, i32_ty, 0);
array_i32 = kit_cg_type_array(c, i32_ty, 4);
@@ -1622,14 +1797,36 @@ int main(void) {
EXPECT(kit_cg_type_ptr_pointee(c, ptr_i32) == i32_ty, "ptr pointee mismatch");
EXPECT(kit_cg_type_array_elem(c, array_i32) == i32_ty, "array elem mismatch");
EXPECT(kit_cg_type_array_count(c, array_i32) == 4, "array count mismatch");
+ huge_array_a = kit_cg_type_array(c, i32_ty, (uint64_t)UINT32_MAX + 1u);
+ huge_array_b = kit_cg_type_array(c, i32_ty, 1);
+ EXPECT(huge_array_a == KIT_CG_TYPE_NONE,
+ "out-of-range array object should be rejected");
+ EXPECT(huge_array_b != KIT_CG_TYPE_NONE,
+ "small array after huge rejection should still work");
alias = kit_cg_type_alias(c, kit_sym_intern(c, KIT_SLICE_LIT("I")), i32_ty);
EXPECT(alias != KIT_CG_TYPE_NONE && alias != i32_ty,
"alias id should be fresh");
EXPECT(kit_cg_type_kind(c, alias) == KIT_CG_TYPE_ALIAS,
"alias kind mismatch");
+ EXPECT(kit_cg_type_resolve_alias(c, alias) == i32_ty,
+ "alias should resolve to base");
EXPECT(kit_cg_type_size(c, alias) == kit_cg_type_size(c, i32_ty),
"alias size mismatch");
+ EXPECT(kit_cg_type_info(c, alias, &info) == KIT_OK, "alias info failed");
+ EXPECT(info.kind == KIT_CG_TYPE_ALIAS && info.storage_id == i32_ty,
+ "alias info exact/storage mismatch");
+ EXPECT(info.layout.valid && info.layout.storage_kind == KIT_CG_STORAGE_INT &&
+ info.layout.scalar_width == 32,
+ "alias storage info mismatch");
+ EXPECT(kit_cg_type_same_storage(c, alias, i32_ty),
+ "alias should share storage with base");
+ alias_ptr_i32 =
+ kit_cg_type_alias(c, kit_sym_intern(c, KIT_SLICE_LIT("PI")), i32_ty);
+ ptr_alias_i32 = kit_cg_type_ptr(c, alias_ptr_i32, 0);
+ EXPECT(ptr_alias_i32 != KIT_CG_TYPE_NONE, "ptr(alias(i32)) failed");
+ EXPECT(kit_cg_type_same_storage(c, ptr_alias_i32, ptr_i32),
+ "ptr(alias(i32)) should share storage with ptr(i32)");
memset(fields, 0, sizeof fields);
fields[0].name = kit_sym_intern(c, KIT_SLICE_LIT("a"));
@@ -1638,10 +1835,15 @@ int main(void) {
fields[1].name = kit_sym_intern(c, KIT_SLICE_LIT("b"));
fields[1].type = ptr_i32;
fields[1].align_override = 1;
- rec = kit_cg_type_record(c, kit_sym_intern(c, KIT_SLICE_LIT("R")), fields, 2);
+ memset(&rdesc, 0, sizeof rdesc);
+ rdesc.tag = kit_sym_intern(c, KIT_SLICE_LIT("R"));
+ rdesc.fields = fields;
+ rdesc.nfields = 2;
+ rec = kit_cg_type_record(c, &rdesc);
EXPECT(rec != KIT_CG_TYPE_NONE, "record type failed");
- EXPECT(kit_cg_type_record(c, kit_sym_intern(c, KIT_SLICE_LIT("Bad")), fields,
- 0) != KIT_CG_TYPE_NONE,
+ memset(&rdesc, 0, sizeof rdesc);
+ rdesc.tag = kit_sym_intern(c, KIT_SLICE_LIT("Bad"));
+ EXPECT(kit_cg_type_record(c, &rdesc) != KIT_CG_TYPE_NONE,
"empty record type failed");
EXPECT(kit_cg_type_kind(c, rec) == KIT_CG_TYPE_RECORD,
"record kind mismatch");
@@ -1662,8 +1864,11 @@ int main(void) {
fields[1].type = i32_ty;
fields[1].flags = KIT_CG_FIELD_BITFIELD;
fields[1].bit_width = 3;
- rec_ex =
- kit_cg_type_record(c, kit_sym_intern(c, KIT_SLICE_LIT("BF")), fields, 2);
+ memset(&rdesc, 0, sizeof rdesc);
+ rdesc.tag = kit_sym_intern(c, KIT_SLICE_LIT("BF"));
+ rdesc.fields = fields;
+ rdesc.nfields = 2;
+ rec_ex = kit_cg_type_record(c, &rdesc);
EXPECT(rec_ex != KIT_CG_TYPE_NONE, "bit-field record type failed");
EXPECT(kit_cg_type_size(c, rec_ex) == 4, "bit-field record size mismatch");
EXPECT(kit_cg_type_record_field(c, rec_ex, 1, &field_out, &field_off) == 0,
@@ -1684,7 +1889,7 @@ int main(void) {
rdesc.nfields = 2;
rdesc.is_union = 1;
rdesc.align_override = 16;
- rec_ex = kit_cg_type_record_ex(c, &rdesc);
+ rec_ex = kit_cg_type_record(c, &rdesc);
EXPECT(rec_ex != KIT_CG_TYPE_NONE, "record desc type failed");
EXPECT(kit_cg_type_size(c, rec_ex) == 16, "record desc size mismatch");
EXPECT(kit_cg_type_align(c, rec_ex) == 16, "record desc align mismatch");
@@ -1692,6 +1897,52 @@ int main(void) {
"record desc field query failed");
EXPECT(field_off == 0, "union field offset mismatch");
+ rec_decl =
+ kit_cg_type_record_decl(c, kit_sym_intern(c, KIT_SLICE_LIT("Node")), 0);
+ EXPECT(rec_decl != KIT_CG_TYPE_NONE, "record decl failed");
+ EXPECT(kit_cg_type_kind(c, rec_decl) == KIT_CG_TYPE_RECORD,
+ "record decl kind mismatch");
+ EXPECT(!kit_cg_type_is_complete(c, rec_decl),
+ "fresh record decl should be incomplete");
+ EXPECT(!kit_cg_type_is_sized(c, rec_decl),
+ "fresh record decl should be unsized");
+ EXPECT(kit_cg_type_record_field(c, rec_decl, 0, NULL, NULL) == KIT_NOT_FOUND,
+ "incomplete record should not expose fields");
+ rec_ptr = kit_cg_type_ptr(c, rec_decl, 0);
+ EXPECT(rec_ptr != KIT_CG_TYPE_NONE,
+ "pointer to incomplete record should be legal");
+ EXPECT(kit_cg_type_array(c, rec_decl, 1) == KIT_CG_TYPE_NONE,
+ "array of incomplete record should fail");
+ memset(fields, 0, sizeof fields);
+ fields[0].name = kit_sym_intern(c, KIT_SLICE_LIT("next"));
+ fields[0].type = rec_ptr;
+ memset(&rdesc, 0, sizeof rdesc);
+ rdesc.tag = kit_cg_type_record_tag(c, rec_decl);
+ rdesc.fields = fields;
+ rdesc.nfields = 1;
+ EXPECT(kit_cg_type_record_complete(c, rec_decl, &rdesc) == KIT_OK,
+ "record completion with self pointer failed");
+ EXPECT(kit_cg_type_is_complete(c, rec_decl),
+ "completed record should be complete");
+ EXPECT(kit_cg_type_is_sized(c, rec_decl), "completed record should be sized");
+ EXPECT(kit_cg_type_record_complete(c, rec_decl, &rdesc) == KIT_INVALID,
+ "record completion should be one-shot");
+
+ rec_bad = kit_cg_type_record_decl(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("ByValueSelf")), 0);
+ memset(fields, 0, sizeof fields);
+ fields[0].name = kit_sym_intern(c, KIT_SLICE_LIT("self"));
+ fields[0].type = rec_bad;
+ memset(&rdesc, 0, sizeof rdesc);
+ rdesc.tag = kit_cg_type_record_tag(c, rec_bad);
+ rdesc.fields = fields;
+ rdesc.nfields = 1;
+ EXPECT(kit_cg_type_record_complete(c, rec_bad, &rdesc) == KIT_INVALID,
+ "recursive by-value record should fail");
+ rec_self_ptr = kit_cg_type_ptr(c, rec_bad, 0);
+ EXPECT(rec_self_ptr != KIT_CG_TYPE_NONE,
+ "pointer to failed incomplete record should still be legal");
+
vals[0].name = kit_sym_intern(c, KIT_SLICE_LIT("A"));
vals[0].value = 1;
vals[1].name = kit_sym_intern(c, KIT_SLICE_LIT("B"));
@@ -1720,6 +1971,17 @@ int main(void) {
EXPECT(kit_cg_type_func_nparams(c, fn) == 2, "function param count mismatch");
EXPECT(kit_cg_type_func_param(c, fn, 1).type == ptr_i32,
"function param mismatch");
+ memset(&sig, 0, sizeof sig);
+ sig.result.type = void_ty;
+ fn = kit_cg_type_func(c, sig);
+ EXPECT(fn != KIT_CG_TYPE_NONE, "void function type failed");
+ EXPECT(kit_cg_type_func_result(c, fn).type == void_ty,
+ "void function result should use void builtin");
+ EXPECT(!kit_cg_func_result_has_value(c, kit_cg_type_func_result(c, fn)),
+ "void function should not have a value result");
+ memset(&sig, 0, sizeof sig);
+ EXPECT(kit_cg_type_func(c, sig) == KIT_CG_TYPE_NONE,
+ "function result NONE should be rejected");
sig.nparams = 70000;
EXPECT(kit_cg_type_func(c, sig) == KIT_CG_TYPE_NONE,
"oversized function param list should fail");
@@ -1734,6 +1996,8 @@ int main(void) {
exercise_cg_literal_folds(c, i32_ty);
exercise_cg_constfold_phases(c, i32_ty, i8_ty);
exercise_cg_memop_constfold_shape(c, i32_ty, i64_ty, ptr_i32);
+ exercise_cg_slot_lang_facts(c, i32_ty);
+ exercise_cg_field_at_store_keep(c, i32_ty, rec);
exercise_cg_memory_mismatch_diags(c, i32_ty, i64_ty, rec);
exercise_compile_session_two_deltas(c);
exercise_cg_begin_end_two_objects(c);
diff --git a/test/arch/inline_public_test.h b/test/arch/inline_public_test.h
@@ -46,8 +46,9 @@ static inline KitTargetSpec it_target(KitArchKind arch) {
static inline KitStatus it_emit_func(KitCompiler* c, void* user) {
InlineEmit* emit = (InlineEmit*)user;
KitCg* cg = NULL;
- KitCgBuiltinTypes bi;
+ KitCgTypeId i64 = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
KitCgFuncSig sig;
+ KitCgFuncResult result;
KitCgDecl decl;
KitCgSym sym;
KitCodeOptions opts;
@@ -57,8 +58,10 @@ static inline KitStatus it_emit_func(KitCompiler* c, void* user) {
memset(&opts, 0, sizeof opts);
if (kit_cg_begin(cg, emit->ob, &opts) != KIT_OK) return KIT_ERR;
- bi = kit_cg_builtin_types(c);
memset(&sig, 0, sizeof sig);
+ memset(&result, 0, sizeof result);
+ result.type = kit_cg_type_builtin(c, KIT_CG_BUILTIN_VOID);
+ sig.result = result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
@@ -72,7 +75,7 @@ static inline KitStatus it_emit_func(KitCompiler* c, void* user) {
if (sym == KIT_CG_SYM_NONE) return KIT_ERR;
kit_cg_func_begin(cg, sym);
- emit->body(c, cg, bi.id[KIT_CG_BUILTIN_I64]);
+ emit->body(c, cg, i64);
kit_cg_ret(cg);
kit_cg_func_end(cg);
if (kit_cg_finish(cg, NULL) != KIT_OK) return KIT_ERR;
diff --git a/test/cg/ir_recorder_test.c b/test/cg/ir_recorder_test.c
@@ -24,7 +24,6 @@ typedef struct TestCtx {
static void tc_init(TestCtx* tc) {
KitTargetSpec target;
- KitCgBuiltinTypes b;
memset(tc, 0, sizeof *tc);
target = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF);
if (kit_unit_compiler_new(&g_u, target, (KitCompiler**)&tc->c) != KIT_OK ||
@@ -32,9 +31,9 @@ static void tc_init(TestCtx* tc) {
fprintf(stderr, "fatal: compiler allocation failed\n");
abort();
}
- b = kit_cg_builtin_types(tc->c);
- tc->i32 = b.id[KIT_CG_BUILTIN_I32];
- tc->ptr = kit_cg_type_ptr(tc->c, b.id[KIT_CG_BUILTIN_VOID], 0);
+ tc->i32 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I32);
+ tc->ptr = kit_cg_type_ptr(tc->c,
+ kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_VOID), 0);
}
static void tc_fini(TestCtx* tc) {
diff --git a/test/cg/native_direct_target_test.c b/test/cg/native_direct_target_test.c
@@ -23,7 +23,6 @@ typedef struct TestCtx {
static void tc_init(TestCtx* tc) {
KitTargetSpec target;
- KitCgBuiltinTypes b;
memset(tc, 0, sizeof *tc);
target = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF);
if (kit_unit_compiler_new(&g_u, target, (KitCompiler**)&tc->c) != KIT_OK ||
@@ -31,9 +30,9 @@ static void tc_init(TestCtx* tc) {
fprintf(stderr, "fatal: compiler allocation failed\n");
abort();
}
- b = kit_cg_builtin_types(tc->c);
- tc->i32 = b.id[KIT_CG_BUILTIN_I32];
- tc->ptr = kit_cg_type_ptr(tc->c, b.id[KIT_CG_BUILTIN_VOID], 0);
+ tc->i32 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I32);
+ tc->ptr = kit_cg_type_ptr(tc->c,
+ kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_VOID), 0);
}
static void tc_fini(TestCtx* tc) {
@@ -57,6 +56,7 @@ typedef enum MockEventKind {
EV_PLAN_CALL,
EV_EMIT_CALL,
EV_PLAN_RET,
+ EV_MOVE,
EV_RET,
} MockEventKind;
@@ -189,6 +189,10 @@ static void mock_binop(NativeTarget* t, BinOp op, NativeLoc dst, NativeLoc a,
ev(mock_of(t), EV_BINOP, op, dst.v.reg, (a.v.reg << 16) | b.v.reg);
}
+static void mock_move_rr(NativeTarget* t, NativeRegLoc dst, NativeRegLoc src) {
+ ev(mock_of(t), EV_MOVE, dst.v.reg, src.v.reg, dst.type);
+}
+
static void mock_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
EXPECT(plan->callee.kind == NATIVE_LOC_REG,
"frame callee should be materialized for call");
@@ -234,6 +238,7 @@ static void mock_native_init(MockNative* m, Compiler* c) {
m->base.store = mock_store;
m->base.load_imm = mock_load_imm;
m->base.binop = mock_binop;
+ m->base.move_rr = mock_move_rr;
m->base.emit_call = mock_emit_call;
m->base.plan_ret = mock_plan_ret;
m->base.ret = mock_ret;
diff --git a/test/cg/strength_reduce_test.c b/test/cg/strength_reduce_test.c
@@ -43,7 +43,6 @@ typedef struct EmitCtx {
static KitStatus emit_binop_fn(KitCompiler* c, void* user) {
EmitCtx* ctx = (EmitCtx*)user;
KitCg* cg = NULL;
- KitCgBuiltinTypes bi;
KitCgTypeId i64_ty;
KitCgFuncParam param_desc;
KitCgFuncResult sig_result;
@@ -60,8 +59,7 @@ static KitStatus emit_binop_fn(KitCompiler* c, void* user) {
opts.opt_level = 0; /* the -O0 peephole is the subject under test */
if (kit_cg_begin(cg, ctx->ob, &opts) != KIT_OK) return KIT_ERR;
- bi = kit_cg_builtin_types(c);
- i64_ty = bi.id[KIT_CG_BUILTIN_I64];
+ i64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
memset(¶m_desc, 0, sizeof param_desc);
param_desc.type = i64_ty;
diff --git a/test/interp/interp_smoke_test.c b/test/interp/interp_smoke_test.c
@@ -39,7 +39,6 @@ typedef struct TestCtx {
static void tc_init(TestCtx* tc) {
KitTargetSpec target;
- KitCgBuiltinTypes b;
memset(tc, 0, sizeof *tc);
target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO);
if (kit_unit_compiler_new(&g_u, target, (KitCompiler**)&tc->c) != KIT_OK ||
@@ -47,10 +46,9 @@ static void tc_init(TestCtx* tc) {
fprintf(stderr, "fatal: compiler allocation failed\n");
abort();
}
- b = kit_cg_builtin_types(tc->c);
- tc->i32 = b.id[KIT_CG_BUILTIN_I32];
- tc->i64 = b.id[KIT_CG_BUILTIN_I64];
- tc->f64 = b.id[KIT_CG_BUILTIN_F64];
+ tc->i32 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I32);
+ tc->i64 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I64);
+ tc->f64 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_F64);
}
static void tc_fini(TestCtx* tc) {
diff --git a/test/opt/cg_ir_lower_test.c b/test/opt/cg_ir_lower_test.c
@@ -27,7 +27,6 @@ typedef struct TestCtx {
static void tc_init(TestCtx* tc) {
KitTargetSpec target;
- KitCgBuiltinTypes b;
memset(tc, 0, sizeof *tc);
target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO);
if (kit_unit_compiler_new(&g_u, target, (KitCompiler**)&tc->c) != KIT_OK ||
@@ -35,8 +34,7 @@ static void tc_init(TestCtx* tc) {
fprintf(stderr, "fatal: compiler allocation failed\n");
abort();
}
- b = kit_cg_builtin_types(tc->c);
- tc->i32 = b.id[KIT_CG_BUILTIN_I32];
+ tc->i32 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I32);
}
static void tc_fini(TestCtx* tc) {
diff --git a/test/opt/tiny_inline_test.c b/test/opt/tiny_inline_test.c
@@ -39,7 +39,6 @@ typedef struct TestCtx {
static void tc_init(TestCtx* tc) {
KitTargetSpec target;
- KitCgBuiltinTypes b;
KitCgFuncSig sig;
KitCgFuncParam params[1];
memset(tc, 0, sizeof *tc);
@@ -49,9 +48,9 @@ static void tc_init(TestCtx* tc) {
fprintf(stderr, "fatal: compiler allocation failed\n");
abort();
}
- b = kit_cg_builtin_types(tc->c);
- tc->i32 = b.id[KIT_CG_BUILTIN_I32];
- tc->ptr = kit_cg_type_ptr(tc->c, b.id[KIT_CG_BUILTIN_VOID], 0);
+ tc->i32 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I32);
+ tc->ptr = kit_cg_type_ptr(tc->c,
+ kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_VOID), 0);
memset(&sig, 0, sizeof sig);
memset(params, 0, sizeof params);
params[0].type = tc->i32;