il: document pointer provenance rules
2f7beb74644448da87f0e894651b62a509db561e294210c9bf0308ebd825a93f
Phase 4: formalize the closed set of pointer-producing instructions in the RIL documentation. - Document the Ptr type distinction in the type system section - Add 'Pointer Provenance' section listing the exhaustive set of instructions that may produce a Ptr-typed register: Reserve, Copy(DataSym/FnAddr), Load with Ptr type, BinOp::Add on Ptr, Call returning Ptr, block parameters, and WordToPtr - Document pointer arithmetic constraints - Annotate Reserve and Copy instructions with provenance semantics
1 parent
e8882a03
lib/std/lang/il.rad
+31 -1
| 12 | 12 | //! > -- "Single-Pass Generation of Static Single-Assignment Form for |
|
| 13 | 13 | //! > Structured Languages", by Marc M. Brandis and Hanspeter Mossenbock |
|
| 14 | 14 | //! |
|
| 15 | 15 | //! # Type System |
|
| 16 | 16 | //! |
|
| 17 | - | //! Primitive types: `W8`, `W16`, `W32` (word sizes). |
|
| 17 | + | //! Primitive types: `W8`, `W16`, `W32`, `W64` (word sizes) and `Ptr`. |
|
| 18 | 18 | //! Signedness is encoded in operations (e.g., `Sdiv` vs `Udiv`), not types. |
|
| 19 | 19 | //! |
|
| 20 | + | //! `Ptr` is the same physical width as `W64` on RV64 but is semantically |
|
| 21 | + | //! distinct: it marks values with pointer provenance. A verifier treats |
|
| 22 | + | //! `Ptr` and `W64` as incompatible types. |
|
| 23 | + | //! |
|
| 20 | 24 | //! Types on arithmetic operations indicate logical width but don't |
|
| 21 | 25 | //! change code generation. Arithmetic is always machine word sized, |
|
| 22 | 26 | //! no 8-bit or 16-bit add instruction. |
|
| 23 | 27 | //! |
|
| 24 | 28 | //! # Truncation and Narrowing |
| 48 | 52 | //! Field access is expressed as address arithmetic plus load/store. |
|
| 49 | 53 | //! |
|
| 50 | 54 | //! Bounds check elimination and nullable pointer optimization happen during |
|
| 51 | 55 | //! this lowering phase. |
|
| 52 | 56 | //! |
|
| 57 | + | //! # Pointer Provenance |
|
| 58 | + | //! |
|
| 59 | + | //! A `Ptr`-typed register may only be produced by the following closed |
|
| 60 | + | //! set of operations: |
|
| 61 | + | //! |
|
| 62 | + | //! | Instruction | Provenance | |
|
| 63 | + | //! |------------------------|------------------------------------------| |
|
| 64 | + | //! | `Reserve` | Stack allocation (local provenance) | |
|
| 65 | + | //! | `Copy(DataSym)` | Global/static data address | |
|
| 66 | + | //! | `Copy(FnAddr)` | Function address (code section) | |
|
| 67 | + | //! | `Load` with `typ: Ptr` | Derived from an existing pointer | |
|
| 68 | + | //! | `BinOp::Add` on `Ptr` | Pointer arithmetic (derived) | |
|
| 69 | + | //! | `Call` returning `Ptr` | Callee-produced pointer | |
|
| 70 | + | //! | Block parameter | Merges pointer values from predecessors | |
|
| 71 | + | //! | `WordToPtr` | Explicit escape hatch (trusted code) | |
|
| 72 | + | //! |
|
| 73 | + | //! Any other instruction producing a register used as a memory base is |
|
| 74 | + | //! a verifier error. `PtrToWord` converts a pointer to `W64`; the |
|
| 75 | + | //! resulting integer cannot be used as a memory base. |
|
| 76 | + | //! |
|
| 77 | + | //! Pointer arithmetic (`BinOp::Add` with `typ: Ptr`) requires exactly |
|
| 78 | + | //! one `Ptr` operand and one integer operand. The result is `Ptr` with |
|
| 79 | + | //! the same provenance as the pointer operand. |
|
| 80 | + | //! |
|
| 53 | 81 | //! # Control Flow |
|
| 54 | 82 | //! |
|
| 55 | 83 | //! Uses block parameters instead of phi nodes (like MLIR/SIL). Each block |
|
| 56 | 84 | //! can declare parameters, and jumps/branches pass arguments to their targets. |
|
| 57 | 85 |
| 196 | 224 | // Memory operations // |
|
| 197 | 225 | /////////////////////// |
|
| 198 | 226 | ||
| 199 | 227 | /// Allocate space on the stack: `reserve %dst <size> <alignment>;` |
|
| 200 | 228 | /// Size can be a register for dynamic stack allocation (eg. VLAs). |
|
| 229 | + | /// The result is always `Ptr` (stack provenance). |
|
| 201 | 230 | Reserve { dst: Reg, size: Val, alignment: u32 }, |
|
| 202 | 231 | /// Load a value from memory (zero-extending): `load <type> %dst %src <offset>;` |
|
| 203 | 232 | Load { typ: Type, dst: Reg, src: Reg, offset: i32 }, |
|
| 204 | 233 | /// Signed load from memory (sign-extending): `sload <type> %dst %src <offset>;` |
|
| 205 | 234 | Sload { typ: Type, dst: Reg, src: Reg, offset: i32 }, |
| 223 | 252 | src: Reg, |
|
| 224 | 253 | /// Size to copy in bytes. |
|
| 225 | 254 | size: Val |
|
| 226 | 255 | }, |
|
| 227 | 256 | /// Copy a value into a register: `copy %dst <val>;`. |
|
| 257 | + | /// When the source is `DataSym` or `FnAddr`, the result is `Ptr`. |
|
| 228 | 258 | Copy { dst: Reg, val: Val }, |
|
| 229 | 259 | ||
| 230 | 260 | ///////////////////// |
|
| 231 | 261 | // ALU operations // |
|
| 232 | 262 | ///////////////////// |