docs: add libVCT public specification set (000-015)
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
# 002 — Traits
|
||||
|
||||
- **Status:** Draft
|
||||
- **Normative language:** `MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` are to be
|
||||
interpreted as described in RFC 2119.
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
The **trait model** is the vocabulary libVCT uses to describe facts about IR nodes. It is the
|
||||
contract between the two optimization halves of the library: HIR discovers and derives traits, and
|
||||
VIR reads them and transforms accordingly. VIR performs no discovery of its own. Everything VIR
|
||||
may rely on is an attribute, a request, or a relationship defined here.
|
||||
|
||||
## 2. Scope
|
||||
|
||||
This file specifies:
|
||||
|
||||
- the `Trait` union of an **Attribute**, a set of **Requests**, and a set of **Relationships**;
|
||||
- the complete v1 attribute set and request catalog;
|
||||
- relationship flags and relation attributes;
|
||||
- the query API semantics a trait consumer may assume;
|
||||
- the trait representation and staleness model;
|
||||
- request conflict resolution.
|
||||
|
||||
Out of scope: how HIR derives attributes (see [004 — HIR Optimizer](004-hir-optimizer.md)), how
|
||||
lowering copies traits onto VIR entities (see [005 — Lowering](005-lowering.md)), and how the VIR
|
||||
optimizer acts on requests (see [007 — VIR Optimizer](007-vir-optimizer.md)).
|
||||
|
||||
Traits are **public**. Frontends embed trait vocabulary; frontends are not required to derive
|
||||
traits themselves.
|
||||
|
||||
## 3. Definitions
|
||||
|
||||
| Term | Definition |
|
||||
|---|---|
|
||||
| **Node** | An arena-owned HIR entity carrying one `Trait`. |
|
||||
| **Attribute** | A fact about a node (its type, value, mutability, and so on). |
|
||||
| **Request** | An attribute-derived suggestion from HIR to VIR. Never ad-hoc. |
|
||||
| **Relationship** | A directed, traceable edge from a node to another node. |
|
||||
| **Suggested** | An attribute or request set by the frontend at build time. |
|
||||
| **Derived** | A trait produced by the HIR optimizer. Authoritative. |
|
||||
| **Epoch** | A monotonically increasing generation counter used to invalidate stale traits. |
|
||||
| **Consumer** | Any component that reads traits. VIR is the primary consumer. |
|
||||
|
||||
The `Trait` is the union of the three sub-structures:
|
||||
|
||||
```d
|
||||
struct Trait {
|
||||
Attribute attr;
|
||||
Request[] reqs;
|
||||
Relation[] rels;
|
||||
}
|
||||
```
|
||||
|
||||
A trait attaches to exactly one node. Trait values are per-node and, after HIR optimization, are
|
||||
complete for every node that lowering consumes.
|
||||
|
||||
## 4. Attribute model
|
||||
|
||||
Attributes are facts about a node. They share **one namespace with two origins**:
|
||||
|
||||
- **Suggested**: set by the frontend at build time through the builder API.
|
||||
- **Derived**: produced by the HIR optimizer during the cascade. Derived attributes are
|
||||
authoritative.
|
||||
|
||||
Every attribute carries an `attr_source` marker recording whether its current value is `suggested`
|
||||
or `derived`.
|
||||
|
||||
### 4.1 Attribute validation rule
|
||||
|
||||
This rule is normative and applies to every suggested attribute:
|
||||
|
||||
1. When the frontend suggests an attribute, HIR **MUST** validate the suggestion against what it
|
||||
can prove.
|
||||
2. If the suggested value is wrong or unprovable-in-the-suggested-direction, HIR **MUST** emit a
|
||||
warning, **MUST** overwrite the attribute with the correct value, and **MUST** mark
|
||||
`attr_source = derived`.
|
||||
3. The warning **MUST** be promoted to an error under `-Werror`. A frontend **MAY** opt out for a
|
||||
specific category with `-Wno-error=<category>`, or for every category with a bare
|
||||
`-Wno-error`.
|
||||
4. A correct suggestion **MUST** be preserved and continue to report `attr_source = suggested`
|
||||
until HIR derives a replacement value.
|
||||
|
||||
HIR **MUST NOT** silently accept a wrong suggestion. A consumer **MUST** treat derived attributes
|
||||
as authoritative and **MUST NOT** re-derive them.
|
||||
|
||||
### 4.2 v1 attribute set
|
||||
|
||||
| Attribute | Meaning | Typical source |
|
||||
|---|---|---|
|
||||
| `ty` | The node's VIR type. | Both |
|
||||
| `const_value` | Present if and only if the value is known at compile time. | Derived |
|
||||
| `is_static` | Frontend-declared compile-time value (the frontend-facing `static` concept). Seeds the comptime evaluator. | Suggested |
|
||||
| `is_comptime` | HIR-proven compile-time value. | Derived |
|
||||
| `is_constant` | Value is constant; not necessarily compile-time-evaluable. | Derived |
|
||||
| `is_used` | Node has at least one use. | Derived |
|
||||
| `is_mutably_used` | Node is mutated through at least one use. | Derived |
|
||||
| `is_addressed` | Address taken via pointer or reference. | Derived |
|
||||
| `escapes` | Value escapes its defining scope. | Derived |
|
||||
| `is_runtime_mutable` | Storage is written at runtime. | Derived |
|
||||
| `may_change_at_runtime` | The observable value may differ between reads or executions (external state). Distinct from `is_runtime_mutable`. | Derived |
|
||||
| `is_volatile` | Accesses are volatile: not eliminated, not reordered, and not promoted to SSA. | Suggested |
|
||||
| `is_atomic` | Accesses are atomic and carry a declared memory ordering. | Suggested |
|
||||
| `complex` | Frontend-provided; gates the `NoOptimize` request. | Suggested |
|
||||
| `layout` | Pinned aggregate ABI: field offsets, total size, and alignment. | Suggested |
|
||||
|
||||
`is_runtime_mutable` describes storage writes; `may_change_at_runtime` describes externally
|
||||
observable value change. They are independent and both **MUST** be tracked.
|
||||
|
||||
`is_static` is trusted by HIR as "this intends to be compile-time", but it is still validated: HIR
|
||||
**MUST** verify the claim before marking `is_comptime`.
|
||||
|
||||
`is_volatile` and `is_atomic` force a value to memory; lowering and later passes **MUST NOT**
|
||||
promote such a value to SSA (see [005 — Lowering](005-lowering.md)). The `layout` attribute pins
|
||||
an aggregate's ABI; the C backend **MUST** emit explicit padding and static assertions for a
|
||||
layout-pinned aggregate (see [008 — C Backend](008-c-backend.md)).
|
||||
|
||||
## 5. Request model
|
||||
|
||||
Requests are attribute-derived suggestions. A frontend suggesting a request that no attribute
|
||||
supports is invalid and HIR **MUST** reject it with a diagnostic.
|
||||
|
||||
Each request has a **strength**:
|
||||
|
||||
- **Soft**: a suggestion VIR **MAY** decline.
|
||||
- **Strong**: violating it is likely a bug. A capable consumer **MUST** honor it or report an
|
||||
error with a `DenyReason` of `Illegality` or `ContradictsTrait` (see
|
||||
[007 — VIR Optimizer](007-vir-optimizer.md)).
|
||||
|
||||
Every attempted request produces a `RequestResult`:
|
||||
|
||||
```d
|
||||
struct RequestResult {
|
||||
Request req;
|
||||
bool accepted;
|
||||
DenyReason reason;
|
||||
string note; // one-line human-readable explanation
|
||||
}
|
||||
```
|
||||
|
||||
A denial **MUST** carry a `DenyReason` and a one-line `note`. On acceptance, `reason` is
|
||||
unspecified and `note` **MAY** be empty.
|
||||
|
||||
### 5.1 DenyReason
|
||||
|
||||
| Enumerator | Meaning |
|
||||
|---|---|
|
||||
| `CostModel` | Profitable only under a different cost model. |
|
||||
| `Illegality` | The transform would be incorrect. |
|
||||
| `AlreadyDone` | No-op; the goal already holds. |
|
||||
| `Unsupported` | VIR does not implement it yet. |
|
||||
| `ContradictsTrait` | Conflicts with a stronger fact. |
|
||||
| `TooLarge` | Exceeds a size budget. |
|
||||
| `NoTargetSupport` | The backend or target cannot express it. |
|
||||
|
||||
## 6. Request catalog
|
||||
|
||||
The v1 catalog contains **42** requests. Requests marked **Strong** are noted; all others are
|
||||
**Soft**.
|
||||
|
||||
### 6.1 Inlining and call edges (10)
|
||||
|
||||
| Request | Strength | Meaning |
|
||||
|---|---|---|
|
||||
| `Inline` | Soft | Callee is a candidate for inlining. |
|
||||
| `AlwaysInline` | **Strong** | Callee must be inlined. |
|
||||
| `NoInline` | **Strong** | Callee must not be inlined. |
|
||||
| `TailCall` | Soft | Prefer tail-call formation. |
|
||||
| `MustTail` | **Strong** | Tail-call formation is required. |
|
||||
| `NoTail` | **Strong** | Tail-call formation must not occur. |
|
||||
| `Devirtualize` | Soft | Call target is expected to resolve to one target. |
|
||||
| `ColdCall` | Soft | Call is unlikely; optimize for size. |
|
||||
| `LikelyCall` | Soft | Call is likely executed. |
|
||||
| `UnlikelyCall` | Soft | Call is unlikely executed. |
|
||||
|
||||
### 6.2 Loops (12)
|
||||
|
||||
| Request | Strength | Meaning |
|
||||
|---|---|---|
|
||||
| `Vectorize` | Soft | Vectorize the loop. |
|
||||
| `Unroll(factor)` | Soft | Unroll by `factor`. |
|
||||
| `Interleave(count)` | Soft | Interleave `count` iterations. |
|
||||
| `Peel(count)` | Soft | Peel `count` iterations. |
|
||||
| `Distribute` | Soft | Distribute loop bodies. |
|
||||
| `Fuse` | Soft | Fuse adjacent loops. |
|
||||
| `Jam` | Soft | Fuse loops by jamming. |
|
||||
| `Unswitch` | Soft | Unswitch loop-invariant conditions. |
|
||||
| `Rotate` | Soft | Rotate the loop. |
|
||||
| `LICM` | Soft | Explicit request to hoist loop-invariant code (an action). |
|
||||
| `LoopInvariant` | Soft | A proven invariant property VIR may exploit (a property). |
|
||||
| `MustProgress` | Soft | The loop is guaranteed to make progress. |
|
||||
|
||||
`LICM` and `LoopInvariant` are distinct: one requests an action, the other asserts a property. Both
|
||||
**MUST** be retained by HIR.
|
||||
|
||||
### 6.3 Memory effects (13)
|
||||
|
||||
| Request | Strength | Meaning |
|
||||
|---|---|---|
|
||||
| `NoAlias` | Soft | The value aliases no other relevant value. |
|
||||
| `Restrict` | Soft | The value may be marked `restrict`. |
|
||||
| `NonNull` | Soft | The value is not null. |
|
||||
| `Align(n)` | Soft | The value is aligned to `n`. |
|
||||
| `ReadOnly` | Soft | Region-level read-only fact. |
|
||||
| `WriteOnly` | Soft | Region-level write-only fact. |
|
||||
| `NoRead` | Soft | Primitive fact on an access edge: no read occurs. |
|
||||
| `NoWrite` | Soft | Primitive fact on an access edge: no write occurs. |
|
||||
| `NoCapture` | Soft | The value is not captured by a callee. |
|
||||
| `Dereferenceable(n)` | Soft | At least `n` bytes are dereferenceable. |
|
||||
| `Constant` | Soft | The value is immutable. |
|
||||
| `NoUndef` | Soft | Reserved; meaningful only on the LLVM path. |
|
||||
| `Prefetch` | Soft | Insert a prefetch hint. |
|
||||
|
||||
`NoRead`/`NoWrite` are **primitives on an access edge**; `ReadOnly`/`WriteOnly` are **region-level
|
||||
facts** derived from them. All four are retained, and their scope difference is part of their
|
||||
meaning.
|
||||
|
||||
### 6.4 Control flow, assumptions, and optimization control (7)
|
||||
|
||||
| Request | Category | Strength | Meaning |
|
||||
|---|---|---|---|
|
||||
| `HotPath` | Control flow | Soft | Block is likely executed. |
|
||||
| `ColdPath` | Control flow | Soft | Block is unlikely executed. |
|
||||
| `Unreachable` | Control flow | Soft | Execution never reaches this point. |
|
||||
| `NoReturn` | Control flow | Soft | The call never returns. |
|
||||
| `Assume(pred)` | Assumptions | Soft | `pred` holds at this point. |
|
||||
| `Range(lo, hi)` | Assumptions | Soft | The value lies in `[lo, hi]`. |
|
||||
| `NoOptimize` | Optimization control | **Strong** | The region must not be rewritten. Requested only when the frontend sets `complex`. |
|
||||
|
||||
HIR **MUST NOT** issue `NoOptimize` unless `complex` is set on the node. The resulting region is an
|
||||
optimization barrier (see [005 — Lowering](005-lowering.md)).
|
||||
|
||||
## 7. Relationships
|
||||
|
||||
Relationships are directed edges that HIR leaves for the non-constant world. They are traceable
|
||||
**up and down** to a terminal node.
|
||||
|
||||
### 7.1 Relationship flags
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `is_offspring` | The edge cannot be traced up. |
|
||||
| `is_ancestor` | The edge cannot be traced down. |
|
||||
| `is_common` | Branch point; a consumer must choose a direction. |
|
||||
| `has_siblings` | The node connects down to two or more nodes. |
|
||||
| `is_apex` | The node participates in a cycle (`a → b → c → d → a`), usually from polymorphism. |
|
||||
|
||||
An apex **SHOULD** normally be ironed out by HIR. A residual apex after the HIR cascade typically
|
||||
arises from polymorphism, and a consumer that walks into one **MUST** handle the cycle rather than
|
||||
loop forever.
|
||||
|
||||
### 7.2 Relation attributes
|
||||
|
||||
Each relationship edge carries a small attribute set:
|
||||
|
||||
| Attribute | Meaning |
|
||||
|---|---|
|
||||
| `was_changed` | The related value changed since the relationship was recorded. |
|
||||
| `is_mutable` | The related value is mutable. |
|
||||
| `is_pointer` | The relationship passes through a pointer. |
|
||||
|
||||
## 8. Query API semantics
|
||||
|
||||
The trait query interface is the **only** channel through which VIR obtains facts. Its guaranteed
|
||||
semantics are:
|
||||
|
||||
1. A query **MUST** return the trait values valid for the node's **current epoch**. A query against
|
||||
a node whose trait is stale for the current epoch **MUST** fail rather than return stale data.
|
||||
2. A query **MUST NOT** trigger analysis or re-derivation. It is a read of already-derived facts.
|
||||
3. Reading `const_value` when absent **MUST** be reported as "not compile-time known" and **MUST
|
||||
NOT** be interpreted as zero or any other value.
|
||||
4. Walking a relationship in the available direction(s) **MUST** terminate at a terminal node or
|
||||
report that the walk hit an apex cycle.
|
||||
5. A consumer **MUST NOT** invent, weaken, or generalize a fact absent from the trait. VIR must not
|
||||
assume a property that no trait states.
|
||||
6. Query results **MUST** be deterministic: for a fixed module and epoch, the same query returns the
|
||||
same result regardless of thread count.
|
||||
|
||||
## 9. Representation
|
||||
|
||||
Representation has two parts: an **embedded dense `Trait` struct** on every node is primary, so
|
||||
common attributes are read without indirection; an **optional sparse side-channel** holds rare or
|
||||
frontend-extensible attributes and **MAY** be absent. A consumer **MUST** observe the same logical
|
||||
trait regardless of which part carries it. The dense struct is the reference representation; the
|
||||
side-channel is an optimization, not a semantic difference.
|
||||
|
||||
## 10. Staleness
|
||||
|
||||
Traits are annotated in place, so a rewrite can invalidate dependent traits. Staleness is resolved
|
||||
with **epoch/generation counters**:
|
||||
|
||||
1. Each node carries an **epoch**. A trait is valid only for the node's **current epoch**.
|
||||
2. A rewrite that changes a trait **MUST** advance the affected node's epoch.
|
||||
3. A rewrite that can affect a related node **MUST** mark that relationship dirty and propagate the
|
||||
invalidation along the relationship edge.
|
||||
4. A derived trait computed from a node whose epoch has advanced **MUST** be recomputed before it is
|
||||
consumed.
|
||||
5. Epoch counters **MUST** be sufficient to keep parallel HIR workers from reading stale traits.
|
||||
This is why the model survives parallel passes.
|
||||
|
||||
## 11. Request conflict resolution
|
||||
|
||||
Requests from independent attributes can conflict. Resolution is normative:
|
||||
|
||||
1. **Strength ordering.** `Strong` beats `Soft`. When a Strong and a Soft request conflict, the
|
||||
Strong request wins and the Soft request is discarded without a diagnostic.
|
||||
2. **Conflicting Strongs.** When two **Strong** requests conflict, HIR **MUST** emit a diagnostic
|
||||
and **MUST** reject the frontend suggestion. The derived, authoritative request wins.
|
||||
3. **Canonical opposing pairs** are `{Inline, AlwaysInline}` against `{NoInline}`, and
|
||||
`{TailCall, MustTail}` against `{NoTail}`. In the named pairs `Inline`/`NoInline` and
|
||||
`TailCall`/`NoTail`, the `No*` member is Strong, so those resolve by rule 1; the corresponding
|
||||
Strong-versus-Strong pairs (`AlwaysInline`/`NoInline`, `MustTail`/`NoTail`) resolve by rule 2.
|
||||
4. A rejected suggestion **MUST NOT** silently disappear; the diagnostic and a remark **MUST**
|
||||
record the rejection.
|
||||
|
||||
## 12. Invariants
|
||||
|
||||
- Every node carries exactly one `Trait`.
|
||||
- Every `RequestResult` denial carries both a `DenyReason` and a non-empty `note`.
|
||||
- A derived attribute is authoritative; consumers **MUST NOT** re-derive it.
|
||||
- `const_value` is present if and only if `is_comptime` holds for the node.
|
||||
- No consumer reads a trait whose epoch is not the node's current epoch.
|
||||
- The trait vocabulary is versioned independently of the textual IR format (see
|
||||
[013 — Build & Packaging](013-build-packaging.md)).
|
||||
|
||||
## 13. Example
|
||||
|
||||
For `y = x + 4` where HIR proves `x` is compile-time `4`, the node `x` carries `is_comptime`,
|
||||
`is_constant`, and `const_value = 4` with `attr_source = derived`; the fold marks downstream nodes
|
||||
`is_comptime` and issues an `Inline` request when the call is proven small. A frontend that had
|
||||
suggested `is_comptime = false` on `x` receives a warning and sees HIR overwrite it. The full
|
||||
walkthrough is normative in [014 — Worked Example](014-worked-example.md).
|
||||
|
||||
## 14. Cross-references
|
||||
|
||||
- [000 — Overview](000-overview.md)
|
||||
- [001 — Architecture](001-architecture.md)
|
||||
- [003 — HIR](003-hir.md)
|
||||
- [004 — HIR Optimizer](004-hir-optimizer.md)
|
||||
- [005 — Lowering](005-lowering.md)
|
||||
- [007 — VIR Optimizer](007-vir-optimizer.md)
|
||||
- [011 — Diagnostics](011-diagnostics.md)
|
||||
- [013 — Build & Packaging](013-build-packaging.md)
|
||||
- [014 — Worked Example](014-worked-example.md)
|
||||
Reference in New Issue
Block a user