Files
2026-05-13 18:16:55 +02:00

125 lines
5.9 KiB
Markdown

# API UX Cleanup Progress
## Status
Implemented and tested the active UX work:
1. generic intrinsic helper,
2. explicit module optimization helper,
3. object/assembly emission convenience,
4. JIT through LLVM-C ORC LLJIT,
5. metadata/debug-info cleanup.
## Implemented APIs
### Phase 1: Generic intrinsic helper
- [x] Added `Builder.intrinsic(name, args, *, overloaded_types=..., name_hint="")`.
- [x] Added no-overload form `Builder.intrinsic(name, args, *, name_hint="")`.
- [x] Internally looks up the intrinsic ID by name.
- [x] Uses `overloaded_types` as LLVM's overload-disambiguation type list.
- [x] Raises a clear error for unknown intrinsic names.
- [x] Raises a clear error when an overloaded intrinsic needs `overloaded_types`.
- [x] Keeps existing lower-level intrinsic APIs public.
- [x] Tests cover non-overloaded, overloaded, and memory intrinsics.
### Phase 2: Module/function optimization helpers
- [x] Added `Module.optimize(pipeline, *, target_machine=None, options=None)`.
- [x] Added `Function.optimize(pipeline, *, target_machine=None, options=None)` for function-level PassBuilder pipelines.
- [x] Uses LLVM PassBuilder pipeline strings directly.
- [x] Mutates the module or function in place.
- [x] Includes the failed pipeline string in error messages.
- [x] Keeps existing lower-level pass APIs public.
- [x] Fixed optional pass options by creating a default `PassBuilderOptions` internally when none is provided.
- [x] Tests cover success and failure paths.
### Phase 3: Object/assembly emission convenience
- [x] Added `TargetMachine.host(...)`.
- [x] Added `Module.emit_object(*, target_machine=None)`.
- [x] Added `Module.emit_assembly(*, target_machine=None)`.
- [x] If `target_machine` is omitted, creates a host target machine internally.
- [x] Emission methods do not run optimization passes.
- [x] Keeps `TargetMachine.emit_to_memory_buffer` public.
- [x] Tests cover object, assembly, host target, explicit target machine, and BinaryManager parsing.
### Phase 4: JIT through LLVM-C
- [x] Inspected available LLVM-C headers and chose ORC LLJIT C API.
- [x] Added `llvm.JIT.host()` context manager.
- [x] Implemented module ownership transfer for `jit.add_module(mod)`.
- [x] `jit.add_module(mod)` invalidates the Python `Module` wrapper on success.
- [x] Added `jit.lookup(name) -> int`.
- [x] Added `jit.ctypes_function(...)` callable wrapper.
- [x] `jit.ctypes_function(...)` keeps the JIT object alive while the returned callable wrapper is alive.
- [x] Added `jit.add_symbol(...)` for integer addresses and ctypes callbacks.
- [x] ctypes callback objects are pinned while the JIT is alive and released on dispose.
- [x] Unsupported host target/JIT setup raises `LLVMError`; tests skip target-dependent checks cleanly when unavailable.
### Phase 5: Metadata/debug-info cleanup
- [x] Added `Value.metadata` mapping view by metadata kind name.
- [x] Added `Metadata.kind`, `is_string`, `is_node`, `is_value`, `string`, `operands`, and `value` accessors.
- [x] Stubbed `Metadata.value` with `NotImplementedError` because LLVM-C cannot unwrap ValueAsMetadata to Value.
- [x] Added `MetadataMap.copy_to(target, include_debug_location=False)` so transforms can copy arbitrary attached metadata without exposing kind IDs.
- [x] Added support for detached instruction metadata by deriving the context from the value type.
- [x] Added `Module.named_metadata` mapping/list view.
- [x] Added `NamedMetadataMap.keys()` and iteration.
- [x] Added `Module.module_flags` view.
- [x] Added `Context.debug_location(...)`.
- [x] Added `Builder.debug_location(...)` context manager.
- [x] Added DIBuilder recipes: `file`, `compile_unit`, `function`, `local_variable`.
- [x] Removed redundant public low-level metadata APIs: raw metadata kind lookup, raw `Value.set_metadata`, public `ValueMetadataEntries`, public `NamedMDNode`, `Metadata.as_value`, raw named-metadata methods, and raw module-flag methods.
- [x] Removed redundant DIBuilder aliases covered by recipes: `create_file`, `create_compile_unit`.
- [x] Kept advanced DIBuilder `create_*` methods where the recipes do not provide full coverage.
## Tests added
- `tests/regressions/test_api_ux_cleanup.py`
- `tests/regressions/test_metadata_ux_cleanup.py`
Coverage:
- [x] non-overloaded intrinsic call,
- [x] overloaded floating-point intrinsic call,
- [x] memory intrinsic call,
- [x] unknown intrinsic error,
- [x] missing overload types error,
- [x] module optimization success and invalid-pipeline failure,
- [x] optimization with target machine,
- [x] object and assembly emission,
- [x] emitted object opens with `BinaryManager`,
- [x] JIT integer function lookup and ctypes call,
- [x] module invalidation after JIT transfer,
- [x] missing JIT symbol error,
- [x] JIT ctypes callable keeps the JIT alive,
- [x] JIT callback symbol registration and callback lifetime pinning.
## Documentation and examples updated
- [x] `README.md` current capabilities, known limitations, and example links.
- [x] `devdocs/api-reference.md` high-level UX helper examples.
- [x] `devdocs/api-ux-cleanup/plan.md` remains the task design reference.
- [x] `examples/intrinsic_memcpy.py` shows `Builder.intrinsic(...)`.
- [x] `examples/optimize_module.py` shows `Module.optimize(...)`.
- [x] `examples/optimize_function.py` shows `Function.optimize(...)`.
- [x] `examples/emit_object_assembly.py` shows `TargetMachine.host()`, `emit_object()`, and `emit_assembly()`.
- [x] `examples/jit_add.py` shows `JIT.host()`, `add_module()`, `lookup` via `ctypes_function()`, and `add_symbol()`.
## Validation performed
```bash
cmake --build build
uv run tests/regressions/test_api_ux_cleanup.py
uv run pytest tests/regressions/test_api_ux_cleanup.py -q
uv run tests/regressions/test_metadata_ux_cleanup.py
uv run pytest tests/regressions/test_metadata_ux_cleanup.py tests/test_examples.py -q
uv run run_tests.py --regressions
uv run run_tests.py
uv run run_llvm_c_tests.py --use-python
uvx ty check
```
All commands passed.