diff --git a/README.md b/README.md
index 52de7df..23aedb5 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,14 @@ uv run python examples/transform_replace_add.py
More examples worth browsing:
+- [`examples/intrinsic_memcpy.py`](examples/intrinsic_memcpy.py) - call an LLVM intrinsic by name with `Builder.intrinsic(...)`
+- [`examples/optimize_module.py`](examples/optimize_module.py) - optimize a module with a PassBuilder pipeline string
+- [`examples/optimize_function.py`](examples/optimize_function.py) - optimize one function with a function-level PassBuilder pipeline string
+- [`examples/emit_object_assembly.py`](examples/emit_object_assembly.py) - emit host object code and assembly from a module
+- [`examples/jit_add.py`](examples/jit_add.py) - JIT-compile IR, call it through ctypes, and register a Python callback
+- [`examples/instruction_metadata.py`](examples/instruction_metadata.py) - attach custom metadata to an instruction and print the IR
+- [`examples/named_metadata.py`](examples/named_metadata.py) - create module named metadata and print the IR
+- [`examples/metadata_debug_info.py`](examples/metadata_debug_info.py) - attach metadata, create debug info, and use debug-location scopes
- [`examples/transform_replace_add.py`](examples/transform_replace_add.py) - simple IR transformation using operands, RAUW, and instruction deletion
- [`examples/bc-stats.py`](examples/bc-stats.py) - print per-function instruction histograms from LLVM IR
- [`examples/bc-graphviz.py`](examples/bc-graphviz.py) - emit a GraphViz-style control-flow graph from LLVM IR
@@ -66,8 +74,10 @@ More examples worth browsing:
- Pythonic wrappers for contexts, modules, types, values, functions, basic blocks, builders, metadata, debug info, object files, targets, target machines, and pass builder options
- IR construction, traversal, and transformation helpers: constants, globals, PHI/control flow/memory/cast/cmp instructions, operands, predecessors, RAUW, split blocks, move/clone/erase instructions
-- IR and bitcode parsing/writing, lazy bitcode modules, module cloning/linking, module flags, diagnostics, attributes, COMDATs, calling conventions, linkage/visibility/storage controls
-- Target initialization/lookup, data layouts, object/assembly emission through target machines, and PassBuilder pipeline execution
+- IR and bitcode parsing/writing, lazy bitcode modules, module cloning/linking, diagnostics, attributes, COMDATs, calling conventions, linkage/visibility/storage controls
+- Metadata/debug-info APIs including named metadata views, module flag views, instruction/global metadata mappings, DIBuilder recipes, and debug-location scopes
+- Target lookup, data layouts, `Module.emit_object()`, `Module.emit_assembly()`, target-machine emission, and PassBuilder pipeline execution via `Module.optimize()`, `Function.optimize()`, and `Module.run_passes()`
+- Generic intrinsic calls with `Builder.intrinsic(...)` and in-process JIT execution through the LLVM-C ORC LLJIT API
- Lifetime/validity guards that turn many use-after-free, disposed-object, null-reference, and wrong-kind mistakes into Python exceptions instead of hard crashes
- Auto-generated typed `.pyi` stubs for IDEs/type checkers
- Golden-master tests, Python regression scripts, examples, and vendored `llvm-c-test` lit tests, including CI coverage against installed wheels
@@ -84,7 +94,7 @@ For development documentation, see `devdocs/README.md`.
## Known limitations
- The API is not stable yet; method names and ownership rules may still change.
-- Scope follows the LLVM-C API, not the full LLVM C++ API. A high-level JIT/ExecutionEngine API is not currently exposed.
+- Scope follows the LLVM-C API, not the full LLVM C++ API. JIT support is intentionally limited to what is exposed cleanly through LLVM-C ORC LLJIT.
- Docs are currently the README, examples, `devdocs/`, and the generated `.pyi` stub; there is no hosted API reference yet.
- Prebuilt wheels currently target CPython 3.12+ stable ABI on Linux x86_64/aarch64, macOS arm64/x86_64, and Windows x86_64. Other platforms require a source build.
diff --git a/devdocs/api-design-philosophy.md b/devdocs/api-design-philosophy.md
index cb26da0..8fda1c7 100644
--- a/devdocs/api-design-philosophy.md
+++ b/devdocs/api-design-philosophy.md
@@ -12,8 +12,8 @@ The earliest API mirrored the LLVM C API directly. While this made initial imple
# Current: Pythonic object-oriented API
i32 = ctx.types.i32
const = i32.constant(42)
-func.attributes.add(attr)
-inst.set_metadata(kind, md)
+func.attributes.add("noreturn")
+inst.metadata["llvm.loop"] = loop_md
```
---
@@ -94,8 +94,10 @@ Python developers expect `object.operation()`, not `module.operation(object)`.
**Acceptable globals**:
- Ownerless factories: `create_context()`
-- Initialization: `initialize_all_targets()`
-- Registry lookups: `get_md_kind_id()` (no object owns the metadata registry)
+- Target and intrinsic registry lookups that do not mutate IR
+
+LLVM target/disassembler registries are initialized when the module is imported;
+public initialization functions add no user-facing value.
Factories with a natural owner should be static methods instead (for example,
`BinaryManager.from_file()` rather than a module-level binary factory).
@@ -124,7 +126,17 @@ This catches errors with clear messages while keeping the API simple.
---
-### 8. Method Chaining Potential
+### 8. C API Anchors in Binding Docs
+
+**Principle**: Every public binding method or property should document the LLVM-C API it uses with a `C API: ...` line.
+
+**Why**: The C API anchor keeps the binding grounded in LLVM's actual surface, makes generated stubs searchable, and lets future maintainers or agents verify behavior against LLVM headers.
+
+If a method is a pure convenience wrapper, list the primary LLVM-C calls it composes. If LLVM-C lacks a required operation, document that as `C API limitation`.
+
+---
+
+### 9. Method Chaining Potential
**Principle**: Methods returning values enable fluent APIs.
diff --git a/devdocs/api-reference.md b/devdocs/api-reference.md
index 9e8ae4f..ac814e5 100644
--- a/devdocs/api-reference.md
+++ b/devdocs/api-reference.md
@@ -38,6 +38,55 @@ rg "replace_all_uses_with|erase_from_parent|split_basic_block|const_string" .ven
- `devdocs/lit-tests.md`
- `devdocs/DEBUGGING.md`
+## High-Level UX Helpers
+
+These APIs wrap common workflows that previously required LLVM-C-style boilerplate:
+
+```python
+# Intrinsics by name.
+builder.intrinsic("llvm.sqrt", [x], overloaded_types=[x.type])
+
+# Explicit PassBuilder pipeline optimization.
+mod.optimize("default", target_machine=tm)
+func.optimize("mem2reg,instcombine,simplifycfg", target_machine=tm)
+
+# Direct object/assembly emission. Optimize explicitly first when desired.
+obj = mod.emit_object(target_machine=tm)
+asm = mod.emit_assembly(target_machine=tm)
+
+# Host target machine convenience.
+tm = llvm.TargetMachine.host()
+
+# LLVM-C ORC LLJIT.
+with llvm.JIT.host() as jit:
+ jit.add_module(mod) # invalidates mod on success
+ addr = jit.lookup("compiled_function")
+
+# Metadata by name, without public kind IDs.
+text = ctx.md_string("frontend")
+assert text.is_string
+assert text.string == "frontend"
+node = ctx.md_node([text])
+for operand in node.operands:
+ assert operand.string == "frontend"
+inst.metadata["llvm.loop"] = loop_md
+md = inst.metadata.get("llvm.loop")
+src_inst.metadata.copy_to(dst_inst)
+del inst.metadata["llvm.loop"]
+
+# Named metadata and module flags.
+mod.named_metadata["llvm.dbg.cu"].append(compile_unit)
+mod.module_flags.add("Debug Info Version", llvm.ModuleFlagBehavior.Warning, md)
+
+# Debug-location scopes.
+with builder.debug_location(line=12, column=4, scope=subprogram):
+ inst = builder.add(a, b, "sum")
+```
+
+`builder.intrinsic(..., overloaded_types=[...])` uses LLVM's intrinsic
+overload-disambiguation type list. This list selects the intrinsic declaration;
+it is not the same as the call operand list.
+
## Value API Validity Matrix
This section documents when `llvm.Value` accessors are valid to call. Invalid
@@ -68,7 +117,7 @@ calls should raise `llvm.LLVMAssertionError` (not crash).
- Global value:
- `global_value_type`, `unnamed_address`
- `linkage`, `visibility`, `dll_storage_class`
- - `global_copy_all_metadata`
+ - `metadata` mapping view; use `value.metadata.copy_to(other)` for bulk copies
- Global object:
- `comdat`, `set_comdat`
- `section`
@@ -90,7 +139,7 @@ calls should raise `llvm.LLVMAssertionError` (not crash).
- Any instruction:
- `opcode`, `opcode_name`
- - `instruction_get_all_metadata_other_than_debug_loc`
+ - `metadata` mapping view; use `inst.metadata.copy_to(other)` for bulk copies
- `next_instruction`, `prev_instruction`
- `remove_from_parent`, `erase_from_parent`, `delete_instruction`,
`instruction_clone`
@@ -216,6 +265,12 @@ This section captures guard preconditions for wrapper classes other than
- `set_body` requires identified opaque struct type (not literal, not already
non-opaque).
+### Type factory aliases
+
+- `types` is available on `Context`, `Module`, `Function`, `BasicBlock`, and `Value`.
+- All aliases for objects in the same context compare equal:
+ `ctx.types == mod.types == fn.types == bb.types == value.types`.
+
### BasicBlock (`llvm.BasicBlock`)
- `terminator` requires the block to have a terminator.
@@ -245,6 +300,9 @@ This section captures guard preconditions for wrapper classes other than
- `return_attributes` for return-value attributes.
- `param_attributes(i)` for parameter attributes, using a 0-based Python index.
- Use `llvm.Attribute.enum/type/string(...)` or `slot.add("noreturn")`.
+ - Use `llvm.Attribute.memory(ctx, "none")` or `slot.add_memory("read")` for `memory(...)` effects without raw encoded integers.
+- `create_builder()` positions in the function entry block and creates an
+ `entry` block when the function has no blocks yet.
- `block_address` requires block ownership by that function.
Prefer `bb.block_address()` when the function can be inferred.
- Parent navigation:
@@ -258,6 +316,9 @@ This section captures guard preconditions for wrapper classes other than
- Positioning:
- `position_before(inst)` requires an instruction attached to a block.
- `position_at(bb, inst)` requires `inst` to be an instruction in `bb`.
+- Memory allocation:
+ - `alloca(ty, name="")` creates a scalar alloca.
+ - `alloca(ty, count, name="")` creates an array alloca.
- Instruction-only insertion helpers:
- `insert_into_builder_with_name(instr)` requires instruction value.
- `add_metadata_to_inst(instr)` requires instruction value.
diff --git a/devdocs/api-ux-cleanup/plan.md b/devdocs/api-ux-cleanup/plan.md
new file mode 100644
index 0000000..b416ee7
--- /dev/null
+++ b/devdocs/api-ux-cleanup/plan.md
@@ -0,0 +1,327 @@
+# API UX Cleanup Plan
+
+## Goal
+
+Make the remaining high-value workflows clean from a Python user's perspective while keeping useful LLVM controls available. This plan covers five areas:
+
+1. generic intrinsic calls,
+2. explicit module optimization by pass pipeline,
+3. object/assembly emission convenience,
+4. JIT through LLVM-C,
+5. metadata/debug-info cleanup.
+
+The API should let users write the operation they mean without requiring LLVM-C boilerplate.
+
+## Design rules
+
+- Keep useful LLVM concepts public.
+- Hide accidental LLVM-C scaffolding from common workflows.
+- Prefer object methods over global helper sequences.
+- Prefer names over registry IDs when the user's input is naturally a name.
+- Prefer explicit ownership rules over implicit invalidation.
+- Raise Python exceptions before LLVM can crash or leave IR half-mutated.
+
+## Phase 1: Generic intrinsic calls
+
+### User problem
+
+Calling an intrinsic currently requires a multi-step sequence: look up an intrinsic ID, get a declaration from a module, construct or know the function type, then call the declaration. That is too much ceremony for a user who already knows the intrinsic name and call operands.
+
+### Target UX
+
+```python
+builder.intrinsic(
+ "llvm.memcpy",
+ [dst, src, n, is_volatile],
+ overloaded_types=[dst.type, src.type, n.type],
+)
+
+builder.intrinsic("llvm.sqrt", [x], overloaded_types=[x.type])
+builder.intrinsic("llvm.trap", [])
+```
+
+### What `overloaded_types` means
+
+`overloaded_types` is LLVM's overload-disambiguation type list. It is not the same as the call argument list.
+
+LLVM has intrinsics whose declarations depend on one or more types. LLVM-C requires those types when retrieving the intrinsic declaration. Examples:
+
+- `llvm.sqrt` is overloaded by floating-point or vector type.
+- overflow intrinsics are overloaded by integer type.
+- memory intrinsics are overloaded by pointer address spaces and length type.
+
+The helper should pass `overloaded_types` to the existing intrinsic declaration machinery internally.
+
+### Public API shape
+
+```python
+builder.intrinsic(
+ name: str,
+ args: Iterable[llvm.Value],
+ *,
+ overloaded_types: Iterable[llvm.Type] = (),
+ name_hint: str = "",
+) -> llvm.Value
+```
+
+### Behavior
+
+- Look up the intrinsic by name internally.
+- Raise `LLVMAssertionError` if the name is unknown.
+- Raise a clear error if the intrinsic is overloaded and `overloaded_types` is empty.
+- Get the intrinsic declaration in the current module.
+- Build and return the call instruction.
+- Keep existing lower-level intrinsic APIs public for advanced users.
+
+### Tests
+
+- Non-overloaded intrinsic call.
+- Overloaded floating-point intrinsic call.
+- Memory intrinsic call.
+- Unknown intrinsic name error.
+- Missing overload types error.
+- Generated module verifies.
+
+## Phase 2: Explicit module/function optimization helpers
+
+### User problem
+
+Running passes is possible, but the current method name is low-level and does not read like a user task. Users should be able to say “optimize this module” or “optimize this function” with a PassBuilder pipeline directly.
+
+### Target UX
+
+```python
+mod.optimize("default")
+mod.optimize("default", target_machine=tm)
+mod.optimize("function(mem2reg),default", target_machine=tm, options=opts)
+
+func.optimize("mem2reg,instcombine,simplifycfg")
+func.optimize("instcombine,simplifycfg", target_machine=tm, options=opts)
+```
+
+### Public API shape
+
+```python
+mod.optimize(
+ pipeline: str,
+ *,
+ target_machine: llvm.TargetMachine | None = None,
+ options: llvm.PassBuilderOptions | None = None,
+) -> None
+
+func.optimize(
+ pipeline: str,
+ *,
+ target_machine: llvm.TargetMachine | None = None,
+ options: llvm.PassBuilderOptions | None = None,
+) -> None
+```
+
+### Behavior
+
+- `Module.optimize` accepts module-level LLVM PassBuilder pipeline strings.
+- `Function.optimize` accepts function-level LLVM PassBuilder pipeline strings.
+- The method mutates the module or function in place.
+- The method wraps the existing pass-running implementation.
+- Existing lower-level pass APIs remain available for advanced users.
+- Error messages should include the failed pipeline string when LLVM rejects it.
+
+### Tests
+
+- `default` succeeds on a simple module.
+- `default` succeeds on a simple module.
+- A custom function pipeline succeeds on one function without changing sibling functions.
+- Invalid pipelines raise Python exceptions with the pipeline in the message.
+- Passing a target machine works.
+
+## Phase 3: Object and assembly emission convenience
+
+### User problem
+
+Emitting object code or assembly is possible, but the common path requires too much setup. Users should be able to emit from a module directly. Optimization should remain a separate explicit step.
+
+### Target UX
+
+```python
+mod.optimize("default", target_machine=tm)
+obj = mod.emit_object(target_machine=tm)
+asm = mod.emit_assembly(target_machine=tm)
+```
+
+For the host target:
+
+```python
+tm = llvm.TargetMachine.host()
+obj = mod.emit_object(target_machine=tm)
+```
+
+### Public API shape
+
+```python
+llvm.TargetMachine.host(
+ cpu: str = "",
+ features: str = "",
+ opt_level: llvm.CodeGenOptLevel = llvm.CodeGenOptLevel.Default,
+ reloc_mode: llvm.RelocMode = llvm.RelocMode.Default,
+ code_model: llvm.CodeModel = llvm.CodeModel.Default,
+) -> llvm.TargetMachine
+
+mod.emit_object(*, target_machine: llvm.TargetMachine | None = None) -> bytes
+mod.emit_assembly(*, target_machine: llvm.TargetMachine | None = None) -> bytes
+```
+
+### Behavior
+
+- If `target_machine` is provided, use it.
+- If `target_machine` is omitted, create a host target machine internally.
+- Do not run optimization passes inside emission methods.
+- Return bytes, matching the existing memory-buffer emission API.
+- Keep `TargetMachine.emit_to_memory_buffer` public for advanced users.
+
+### Tests
+
+- Emit object for a simple function.
+- Emit assembly for a simple function.
+- Emitted object can be opened by `BinaryManager`.
+- Explicit `target_machine=` path works.
+- Omitted `target_machine` path works when host target support is available.
+- Missing target support raises a clear Python exception or skips in tests where appropriate.
+
+## Phase 4: JIT through LLVM-C
+
+### User problem
+
+In-process JIT execution is not available. This blocks DSL/compiler workflows that need to compile IR and call generated code from Python.
+
+### Constraint
+
+Use LLVM-C APIs only. The implementation must not bind LLVM C++ ORC or ExecutionEngine classes directly.
+
+The first implementation step is to inspect the LLVM-C headers available in this project build and choose the best supported C API:
+
+- ORC/LLJIT C API if available and stable enough,
+- otherwise MCJIT/ExecutionEngine C API if available.
+
+### Target UX
+
+```python
+with llvm.JIT.host() as jit:
+ jit.add_module(mod)
+ addr = jit.lookup("add_i32")
+```
+
+ctypes convenience:
+
+```python
+with llvm.JIT.host() as jit:
+ jit.add_module(mod)
+ add_i32 = jit.ctypes_function(
+ "add_i32",
+ restype=ctypes.c_int32,
+ argtypes=[ctypes.c_int32, ctypes.c_int32],
+ )
+ assert add_i32(2, 3) == 5
+```
+
+Callback support if the chosen LLVM-C API exposes symbol registration cleanly:
+
+```python
+callback = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_int32)(py_func)
+jit.add_symbol("py_func", callback)
+```
+
+### Ownership rules
+
+- `jit.add_module(mod)` transfers module ownership to the JIT.
+- The Python `Module` wrapper becomes invalid after transfer.
+- `jit.lookup(name)` returns an integer address.
+- `jit.ctypes_function(...)` wraps `lookup` and keeps required owner references alive.
+- ctypes callback objects registered with the JIT must be pinned for at least as long as the JIT can call them.
+
+### Tests
+
+- JIT a pure integer function and call it through ctypes.
+- Lookup of a missing symbol raises a clean exception.
+- Module wrapper use after transfer raises a memory/lifetime error.
+- Callback test if symbol registration is implemented.
+- Unsupported platforms skip cleanly.
+
+## Phase 5: Metadata/debug-info cleanup
+
+### User problem
+
+Common metadata operations should not require numeric metadata kind IDs or raw LLVM-C named-metadata handles.
+
+### Target UX
+
+```python
+inst.metadata["llvm.loop"] = loop_md
+md = inst.metadata.get("llvm.loop")
+del inst.metadata["llvm.loop"]
+
+mod.named_metadata["llvm.dbg.cu"].append(compile_unit)
+for md in mod.named_metadata["llvm.dbg.cu"]:
+ ...
+
+mod.module_flags.add("Debug Info Version", llvm.ModuleFlagBehavior.Warning, value_md)
+
+loc = ctx.debug_location(line=12, column=4, scope=subprogram)
+with builder.debug_location(loc):
+ value = builder.add(a, b, "sum")
+```
+
+### Behavior
+
+- Hide metadata kind IDs from normal use.
+- Provide a bulk metadata copy path for transforms without exposing kind IDs.
+- Expose named metadata as a mapping from name to appendable list.
+- Expose module flags as a keyed view.
+- Keep advanced DIBuilder creation methods that model useful debug-info concepts.
+- Remove redundant raw APIs where the new views have parity.
+
+### Tests
+
+- Metadata set/get/delete by name.
+- Detached instruction metadata.
+- Metadata bulk copy.
+- Named metadata append, iteration, key iteration.
+- Module flags add/get/key iteration.
+- Debug-location context manager.
+- DIBuilder recipes for file, compile unit, function, and local variable.
+- Public surface is reviewed during API changes.
+
+## API audit process
+
+After each phase:
+
+1. Rebuild to regenerate `.pyi`.
+2. Review new public symbols.
+3. Confirm examples express user tasks directly.
+4. Keep advanced lower-level APIs when they correspond to useful LLVM controls.
+
+## Suggested implementation order
+
+1. Add `Builder.intrinsic(...)`.
+2. Add `Module.optimize(...)`.
+3. Add `TargetMachine.host()`.
+4. Add `Module.emit_object(...)` and `Module.emit_assembly(...)`.
+5. Design and implement `llvm.JIT` using LLVM-C.
+6. Add metadata/debug-info mapping views and DIBuilder recipes.
+
+## Acceptance criteria
+
+- Intrinsic examples use `builder.intrinsic(...)` for generic intrinsic calls.
+- Optimization examples use `mod.optimize()`.
+- Object/assembly examples use `mod.emit_object()` / `mod.emit_assembly()`.
+- JIT design and implementation use LLVM-C only.
+- Metadata examples avoid raw metadata kind IDs.
+- Low-level APIs remain available when they are useful advanced controls.
+- Tests pass:
+
+```bash
+cmake --build build
+uv run run_tests.py
+uv run run_tests.py --regressions
+uv run run_llvm_c_tests.py --use-python
+uvx ty check
+```
diff --git a/devdocs/api-ux-cleanup/progress.md b/devdocs/api-ux-cleanup/progress.md
new file mode 100644
index 0000000..8ba5c8a
--- /dev/null
+++ b/devdocs/api-ux-cleanup/progress.md
@@ -0,0 +1,124 @@
+# 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.
diff --git a/examples/emit_object_assembly.py b/examples/emit_object_assembly.py
new file mode 100644
index 0000000..b6ae51c
--- /dev/null
+++ b/examples/emit_object_assembly.py
@@ -0,0 +1,61 @@
+"""Emit object code and assembly from a module.
+
+The module is optimized explicitly before emission. ``emit_object`` and
+``emit_assembly`` perform code generation only.
+
+Run from the repository root with:
+
+ uv run python examples/emit_object_assembly.py
+"""
+
+from __future__ import annotations
+
+import llvm
+
+
+def emit_for_host() -> tuple[str, int, str, list[str]]:
+ tm = llvm.TargetMachine.host()
+
+ with llvm.create_context() as ctx:
+ i32 = ctx.types.i32
+
+ with ctx.create_module("emit_example") as mod:
+ mod.target_triple = tm.triple
+ mod.data_layout = str(tm.create_data_layout())
+
+ fn = mod.add_function("get_answer", ctx.types.function(i32, []))
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.ret(i32.constant(42))
+
+ assert mod.verify(), mod.verification_error
+ mod.optimize("default", target_machine=tm)
+
+ obj = mod.emit_object(target_machine=tm)
+ asm = mod.emit_assembly(target_machine=tm)
+
+ with llvm.BinaryManager.from_bytes(obj) as binary:
+ binary_type = binary.type.name
+
+ asm_lines = asm.decode("utf-8", errors="replace").splitlines()
+ preview = [line for line in asm_lines if line.strip()][:6]
+ return tm.triple, len(obj), binary_type, preview
+
+
+def main() -> None:
+ try:
+ triple, obj_size, binary_type, preview = emit_for_host()
+ except llvm.LLVMError as exc:
+ print(f"skipped: host code generation is unavailable: {exc}")
+ return
+
+ print(f"target: {triple}")
+ print(f"object bytes: {obj_size}")
+ print(f"binary type: {binary_type}")
+ print("assembly preview:")
+ for line in preview:
+ print(f" {line}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/instruction_metadata.py b/examples/instruction_metadata.py
new file mode 100644
index 0000000..6340a3e
--- /dev/null
+++ b/examples/instruction_metadata.py
@@ -0,0 +1,57 @@
+"""Attach custom metadata to an instruction and print LLVM IR.
+
+Run from the repository root with:
+
+ uv run python examples/instruction_metadata.py
+"""
+
+from __future__ import annotations
+
+import llvm
+
+
+def build_module() -> str:
+ with llvm.create_context() as ctx:
+ i32 = ctx.types.i32
+ fn_type = ctx.types.function(i32, [i32])
+
+ with ctx.create_module("instruction_metadata_example") as mod:
+ fn = mod.add_function("bump", fn_type)
+ entry = fn.append_basic_block("entry")
+
+ with entry.create_builder() as builder:
+ result = builder.add(fn.get_param(0), i32.constant(1), "result")
+
+ # Metadata is addressed by its kind name. No metadata kind ID is
+ # exposed in the Python API.
+ note_text = ctx.md_string("created by instruction_metadata.py")
+ note = ctx.md_node([note_text])
+ result.metadata["example.note"] = note
+
+ # Inspect the attached metadata through the same mapping view.
+ attached_note = result.metadata["example.note"]
+ has_note = "example.note" in result.metadata
+ round_trip_matches = result.metadata.get("example.note") == note
+ note_operand_count = len(attached_note.operands)
+ note_text_value = attached_note.operands[0].string
+
+ builder.ret(result)
+
+ assert mod.verify(), mod.verification_error
+ return (
+ "instruction metadata:\n"
+ f" has example.note: {has_note}\n"
+ f" round trip matches: {round_trip_matches}\n"
+ f" note operands: {note_operand_count}\n"
+ f" note text: {note_text_value}\n"
+ "\nIR:\n"
+ f"{mod}"
+ )
+
+
+def main() -> None:
+ print(build_module(), end="")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/intrinsic_memcpy.py b/examples/intrinsic_memcpy.py
new file mode 100644
index 0000000..26b687e
--- /dev/null
+++ b/examples/intrinsic_memcpy.py
@@ -0,0 +1,52 @@
+"""Build a call to an LLVM intrinsic by name.
+
+This example uses ``Builder.intrinsic`` for ``llvm.memcpy``. The
+``overloaded_types`` list selects the intrinsic declaration:
+
+- destination pointer type,
+- source pointer type,
+- length integer type.
+
+Run from the repository root with:
+
+ uv run python examples/intrinsic_memcpy.py
+"""
+
+from __future__ import annotations
+
+import llvm
+
+
+def build_module() -> str:
+ with llvm.create_context() as ctx:
+ void = ctx.types.void
+ ptr = ctx.types.ptr
+ i64 = ctx.types.i64
+ i1 = ctx.types.i1
+
+ with ctx.create_module("intrinsic_memcpy_example") as mod:
+ fn_ty = ctx.types.function(void, [ptr, ptr, i64])
+ fn = mod.add_function("copy_bytes", fn_ty)
+ dst = fn.get_param(0)
+ src = fn.get_param(1)
+ count = fn.get_param(2)
+
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.intrinsic(
+ "llvm.memcpy",
+ [dst, src, count, i1.constant(False)],
+ overloaded_types=[ptr, ptr, i64],
+ )
+ builder.ret_void()
+
+ assert mod.verify(), mod.verification_error
+ return str(mod)
+
+
+def main() -> None:
+ print(build_module(), end="")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/jit_add.py b/examples/jit_add.py
new file mode 100644
index 0000000..99f9d51
--- /dev/null
+++ b/examples/jit_add.py
@@ -0,0 +1,83 @@
+"""Compile LLVM IR in-process and call it from Python.
+
+This example uses the LLVM-C ORC LLJIT wrapper. ``jit.add_module(mod)``
+transfers the module into the JIT and invalidates the Python ``Module`` wrapper
+on success.
+
+Run from the repository root with:
+
+ uv run python examples/jit_add.py
+"""
+
+from __future__ import annotations
+
+import ctypes
+from typing import cast
+
+import llvm
+
+
+CALLBACK_TYPE = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_int32)
+
+
+@CALLBACK_TYPE
+def py_add_ten(value: int) -> int:
+ return value + 10
+
+
+def add_functions_to_module(ctx: llvm.Context, mod: llvm.Module) -> None:
+ i32 = ctx.types.i32
+
+ add_fn = mod.add_function("add_i32", ctx.types.function(i32, [i32, i32]))
+ lhs = add_fn.get_param(0)
+ rhs = add_fn.get_param(1)
+ add_entry = add_fn.append_basic_block("entry")
+ with add_entry.create_builder() as builder:
+ builder.ret(builder.add(lhs, rhs, "sum"))
+
+ callback_decl = mod.add_function("py_add_ten", ctx.types.function(i32, [i32]))
+ call_python_fn = mod.add_function("call_python", ctx.types.function(i32, [i32]))
+ arg = call_python_fn.get_param(0)
+ callback_entry = call_python_fn.append_basic_block("entry")
+ with callback_entry.create_builder() as builder:
+ result = builder.call(callback_decl, [arg], "result")
+ builder.ret(result)
+
+
+def run_jit() -> tuple[int, int]:
+ with llvm.JIT.host() as jit:
+ jit.add_symbol("py_add_ten", py_add_ten)
+
+ with llvm.create_context() as ctx:
+ with ctx.create_module("jit_example") as mod:
+ add_functions_to_module(ctx, mod)
+ assert mod.verify(), mod.verification_error
+ jit.add_module(mod)
+
+ add_i32 = jit.ctypes_function(
+ "add_i32",
+ restype=ctypes.c_int32,
+ argtypes=[ctypes.c_int32, ctypes.c_int32],
+ )
+ call_python = jit.ctypes_function(
+ "call_python",
+ restype=ctypes.c_int32,
+ argtypes=[ctypes.c_int32],
+ )
+
+ return cast(int, add_i32(40, 2)), cast(int, call_python(5))
+
+
+def main() -> None:
+ try:
+ add_result, callback_result = run_jit()
+ except llvm.LLVMError as exc:
+ print(f"skipped: host JIT is unavailable: {exc}")
+ return
+
+ print(f"add_i32(40, 2) = {add_result}")
+ print(f"call_python(5) = {callback_result}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/metadata_debug_info.py b/examples/metadata_debug_info.py
new file mode 100644
index 0000000..9b3bf1a
--- /dev/null
+++ b/examples/metadata_debug_info.py
@@ -0,0 +1,77 @@
+"""Use Pythonic metadata and debug-info helpers.
+
+This example shows:
+
+- instruction metadata by kind name,
+- named metadata as a module mapping,
+- module flags as a keyed view,
+- DIBuilder convenience methods,
+- builder debug-location scopes.
+
+Run from the repository root with:
+
+ uv run python examples/metadata_debug_info.py
+"""
+
+from __future__ import annotations
+
+import llvm
+
+
+def build_module() -> str:
+ with llvm.create_context() as ctx:
+ with ctx.create_module("metadata_debug_info_example") as mod:
+ mod.is_new_dbg_info_format = True
+ i32 = ctx.types.i32
+ fn = mod.add_function("add", ctx.types.function(i32, [i32, i32]))
+
+ with mod.create_dibuilder() as dib:
+ file = dib.file("main.c", ".")
+ compile_unit = dib.compile_unit(
+ language=llvm.DwarfLanguage.C,
+ file=file,
+ producer="llvm-nanobind example",
+ )
+ subprogram = dib.function(
+ fn,
+ name="add",
+ file=file,
+ line=1,
+ return_type=i32,
+ param_types=[i32, i32],
+ )
+ _tmp_var = dib.local_variable(subprogram, "tmp", file, 2, i32)
+
+ # Named metadata is a mapping from name to appendable list.
+ mod.named_metadata["example.compile_units"].append(compile_unit)
+
+ # Module flags are keyed by their string name.
+ mod.module_flags.add(
+ "Example Metadata Version",
+ llvm.ModuleFlagBehavior.Warning,
+ i32.constant(1).as_metadata(),
+ )
+
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ with builder.debug_location(line=2, column=5, scope=subprogram):
+ result = builder.add(fn.get_param(0), fn.get_param(1), "result")
+
+ # Metadata kind lookup is internal to the mapping view.
+ result.metadata["example.note"] = ctx.md_node(
+ [ctx.md_string("created by metadata_debug_info.py")]
+ )
+ builder.ret(result)
+
+ dib.finalize()
+
+ assert mod.verify(), mod.verification_error
+ return str(mod)
+
+
+def main() -> None:
+ print(build_module(), end="")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/named_metadata.py b/examples/named_metadata.py
new file mode 100644
index 0000000..1d4e4ef
--- /dev/null
+++ b/examples/named_metadata.py
@@ -0,0 +1,49 @@
+"""Create module named metadata and print LLVM IR.
+
+Run from the repository root with:
+
+ uv run python examples/named_metadata.py
+"""
+
+from __future__ import annotations
+
+import llvm
+
+
+def build_module() -> str:
+ with llvm.create_context() as ctx:
+ with ctx.create_module("named_metadata_example") as mod:
+ tags = mod.named_metadata["example.tags"]
+ tags.append(ctx.md_node([ctx.md_string("frontend"), ctx.md_string("demo")]))
+ tags.append(ctx.md_node([ctx.md_string("purpose"), ctx.md_string("docs")]))
+
+ # Inspect named metadata through the module mapping view.
+ keys = list(mod.named_metadata)
+ operand_count = len(mod.named_metadata["example.tags"])
+ iterated_count = sum(1 for _ in mod.named_metadata["example.tags"])
+
+ first_tag = mod.named_metadata["example.tags"][0]
+ first_tag_strings = [operand.string for operand in first_tag.operands]
+
+ assert keys == ["example.tags"]
+ assert operand_count == 2
+ assert iterated_count == 2
+ assert first_tag_strings == ["frontend", "demo"]
+ assert mod.verify(), mod.verification_error
+ return (
+ "named metadata:\n"
+ f" keys: {', '.join(keys)}\n"
+ f" example.tags operands: {operand_count}\n"
+ f" iterated operands: {iterated_count}\n"
+ f" first tag strings: {', '.join(first_tag_strings)}\n"
+ "\nIR:\n"
+ f"{mod}"
+ )
+
+
+def main() -> None:
+ print(build_module(), end="")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/optimize_function.py b/examples/optimize_function.py
new file mode 100644
index 0000000..f103c8b
--- /dev/null
+++ b/examples/optimize_function.py
@@ -0,0 +1,57 @@
+"""Optimize one function with an LLVM PassBuilder function pipeline string.
+
+``Function.optimize`` mutates only that function in place. The pipeline string is
+a function-level PassBuilder pipeline, such as ``mem2reg,instcombine,simplifycfg``.
+
+Run from the repository root with:
+
+ uv run python examples/optimize_function.py
+"""
+
+from __future__ import annotations
+
+import textwrap
+
+import llvm
+
+
+INPUT_IR = """
+define i32 @optimize_me(i32 %x) {
+entry:
+ %tmp = alloca i32
+ %zero = add i32 %x, 0
+ store i32 %zero, ptr %tmp
+ %loaded = load i32, ptr %tmp
+ %result = mul i32 %loaded, 1
+ ret i32 %result
+}
+
+define i32 @leave_me_alone(i32 %x) {
+entry:
+ %result = add i32 %x, 0
+ ret i32 %result
+}
+"""
+
+
+def optimize_one_function(
+ ir_text: str,
+ function_name: str = "optimize_me",
+ pipeline: str = "mem2reg,instcombine,simplifycfg",
+) -> str:
+ with llvm.create_context() as ctx:
+ with ctx.parse_ir(textwrap.dedent(ir_text).strip() + "\n") as mod:
+ assert mod.verify(), mod.verification_error
+ fn = mod.get_function(function_name)
+ assert fn is not None
+ fn.optimize(pipeline)
+ assert mod.verify(), mod.verification_error
+ return str(mod)
+
+
+def main() -> None:
+ print(optimize_one_function(INPUT_IR), end="")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/optimize_module.py b/examples/optimize_module.py
new file mode 100644
index 0000000..d6a95f5
--- /dev/null
+++ b/examples/optimize_module.py
@@ -0,0 +1,46 @@
+"""Optimize a module with an LLVM PassBuilder pipeline string.
+
+``Module.optimize`` mutates the module in place. The pipeline string is the same
+string accepted by LLVM's PassBuilder, such as ``default`` or
+``default``.
+
+Run from the repository root with:
+
+ uv run python examples/optimize_module.py
+"""
+
+from __future__ import annotations
+
+import textwrap
+
+import llvm
+
+
+INPUT_IR = """
+define i32 @add_then_simplify(i32 %x) {
+entry:
+ %tmp = alloca i32
+ %zero = add i32 %x, 0
+ store i32 %zero, ptr %tmp
+ %loaded = load i32, ptr %tmp
+ %result = mul i32 %loaded, 1
+ ret i32 %result
+}
+"""
+
+
+def optimize_ir(ir_text: str, pipeline: str = "default") -> str:
+ with llvm.create_context() as ctx:
+ with ctx.parse_ir(textwrap.dedent(ir_text).strip() + "\n") as mod:
+ assert mod.verify(), mod.verification_error
+ mod.optimize(pipeline)
+ assert mod.verify(), mod.verification_error
+ return str(mod)
+
+
+def main() -> None:
+ print(optimize_ir(INPUT_IR), end="")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/llvm-c/OCaml/.ocamlformat b/llvm-c/OCaml/.ocamlformat
deleted file mode 100644
index e69de29..0000000
diff --git a/llvm-c/OCaml/Utils/Testsuite.ml b/llvm-c/OCaml/Utils/Testsuite.ml
deleted file mode 100644
index 7a8955a..0000000
--- a/llvm-c/OCaml/Utils/Testsuite.ml
+++ /dev/null
@@ -1,33 +0,0 @@
-(* Tiny unit test framework - really just to help find which line is busted *)
-let exit_status = ref 0
-
-let suite_name = ref ""
-
-let group_name = ref ""
-
-let case_num = ref 0
-
-let print_checkpoints = false
-
-let group name =
- group_name := !suite_name ^ "/" ^ name;
- case_num := 0;
- if print_checkpoints then prerr_endline (" " ^ name ^ "...")
-
-let insist ?(exit_on_fail = false) cond =
- incr case_num;
- if not cond then exit_status := 10;
- ( match (print_checkpoints, cond) with
- | false, true -> ()
- | false, false ->
- prerr_endline
- ( "FAILED: " ^ !suite_name ^ "/" ^ !group_name ^ " #"
- ^ string_of_int !case_num )
- | true, true -> prerr_endline (" " ^ string_of_int !case_num)
- | true, false -> prerr_endline (" " ^ string_of_int !case_num ^ " FAIL") );
- if exit_on_fail && not cond then exit !exit_status else ()
-
-let suite name f =
- suite_name := name;
- if print_checkpoints then prerr_endline (name ^ ":");
- f ()
diff --git a/llvm-c/OCaml/Utils/lit.local.cfg b/llvm-c/OCaml/Utils/lit.local.cfg
deleted file mode 100644
index a8c015b..0000000
--- a/llvm-c/OCaml/Utils/lit.local.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-# This is a directory for utility functions. No test here.
-config.suffixes = [".dummy"]
diff --git a/llvm-c/OCaml/analysis.ml b/llvm-c/OCaml/analysis.ml
deleted file mode 100644
index da3e662..0000000
--- a/llvm-c/OCaml/analysis.ml
+++ /dev/null
@@ -1,54 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/analysis.ml
- * RUN: %ocamlc -g -w +A -package llvm.analysis -linkpkg %t/analysis.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.analysis -linkpkg %t/analysis.ml -o %t/executable
- * RUN: %t/executable
- * XFAIL: vg_leak
- *)
-
-open Llvm
-open Llvm_analysis
-
-(* Note that this takes a moment to link, so it's best to keep the number of
- individual tests low. *)
-
-let context = global_context ()
-
-let test x = if not x then exit 1 else ()
-
-let bomb msg =
- prerr_endline msg;
- exit 2
-
-let _ =
- let fty = function_type (void_type context) [| |] in
- let m = create_module context "valid_m" in
- let fn = define_function "valid_fn" fty m in
- let at_entry = builder_at_end context (entry_block fn) in
- ignore (build_ret_void at_entry);
-
-
- (* Test that valid constructs verify. *)
- begin match verify_module m with
- Some msg -> bomb "valid module failed verification!"
- | None -> ()
- end;
-
- if not (verify_function fn) then bomb "valid function failed verification!";
-
-
- (* Test that invalid constructs do not verify.
- A basic block can contain only one terminator instruction. *)
- ignore (build_ret_void at_entry);
-
- begin match verify_module m with
- Some msg -> ()
- | None -> bomb "invalid module passed verification!"
- end;
-
- if verify_function fn then bomb "invalid function passed verification!";
-
-
- dispose_module m
-
- (* Don't bother to test assert_valid_{module,function}. *)
diff --git a/llvm-c/OCaml/bitreader.ml b/llvm-c/OCaml/bitreader.ml
deleted file mode 100644
index 2638ca9..0000000
--- a/llvm-c/OCaml/bitreader.ml
+++ /dev/null
@@ -1,83 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/bitreader.ml
- * RUN: %ocamlc -g -w +A -package llvm.bitreader -package llvm.bitwriter -linkpkg %t/bitreader.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * RUN: %ocamlopt -g -w +A -package llvm.bitreader -package llvm.bitwriter -linkpkg %t/bitreader.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * RUN: llvm-dis < %t/bitcode.bc
- * XFAIL: vg_leak
- *)
-
-(* Note that this takes a moment to link, so it's best to keep the number of
- individual tests low. *)
-
-let context = Llvm.global_context ()
-
-let diagnostic_handler _ = ()
-
-let test x = if not x then exit 1 else ()
-
-let _ =
- Llvm.set_diagnostic_handler context (Some diagnostic_handler);
-
- let fn = Sys.argv.(1) in
- let m = Llvm.create_module context "ocaml_test_module" in
-
- test (Llvm_bitwriter.write_bitcode_file m fn);
-
- Llvm.dispose_module m;
-
- (* parse_bitcode *)
- begin
- let mb = Llvm.MemoryBuffer.of_file fn in
- begin try
- let m = Llvm_bitreader.parse_bitcode context mb in
- Llvm.dispose_module m
- with x ->
- Llvm.MemoryBuffer.dispose mb;
- raise x
- end
- end;
-
- (* MemoryBuffer.of_file *)
- test begin try
- let mb = Llvm.MemoryBuffer.of_file (fn ^ ".bogus") in
- Llvm.MemoryBuffer.dispose mb;
- false
- with Llvm.IoError _ ->
- true
- end;
-
- (* get_module *)
- begin
- let mb = Llvm.MemoryBuffer.of_file fn in
- let m = begin try
- Llvm_bitreader.get_module context mb
- with x ->
- Llvm.MemoryBuffer.dispose mb;
- raise x
- end in
- Llvm.dispose_module m
- end;
-
- (* corrupt the bitcode *)
- let fn = fn ^ ".txt" in
- begin let oc = open_out fn in
- output_string oc "not a bitcode file\n";
- close_out oc
- end;
-
- (* test get_module exceptions *)
- test begin
- try
- let mb = Llvm.MemoryBuffer.of_file fn in
- let m = begin try
- Llvm_bitreader.get_module context mb
- with x ->
- Llvm.MemoryBuffer.dispose mb;
- raise x
- end in
- Llvm.dispose_module m;
- false
- with Llvm_bitreader.Error _ ->
- true
- end
diff --git a/llvm-c/OCaml/bitwriter.ml b/llvm-c/OCaml/bitwriter.ml
deleted file mode 100644
index 17111bd..0000000
--- a/llvm-c/OCaml/bitwriter.ml
+++ /dev/null
@@ -1,49 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/bitwriter.ml
- * RUN: %ocamlc -g -w -3 -w +A -package llvm.bitreader -package llvm.bitwriter -linkpkg %t/bitwriter.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * RUN: %ocamlopt -g -w -3 -w +A -package llvm.bitreader -package llvm.bitwriter -linkpkg %t/bitwriter.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * RUN: llvm-dis < %t/bitcode.bc
- * XFAIL: vg_leak
- *)
-
-(* Note that this takes a moment to link, so it's best to keep the number of
- individual tests low. *)
-
-let context = Llvm.global_context ()
-
-let test x = if not x then exit 1 else ()
-
-let read_file name =
- let ic = open_in_bin name in
- let len = in_channel_length ic in
- let buf = Bytes.create len in
-
- test ((input ic buf 0 len) = len);
-
- close_in ic;
-
- buf
-
-let temp_bitcode ?unbuffered m =
- let temp_name, temp_oc = Filename.open_temp_file ~mode:[Open_binary] "" "" in
-
- test (Llvm_bitwriter.output_bitcode ?unbuffered temp_oc m);
- flush temp_oc;
-
- let temp_buf = read_file temp_name in
-
- close_out temp_oc;
-
- temp_buf
-
-let _ =
- let m = Llvm.create_module context "ocaml_test_module" in
-
- test (Llvm_bitwriter.write_bitcode_file m Sys.argv.(1));
- let file_buf = read_file Sys.argv.(1) in
-
- test (file_buf = temp_bitcode m);
- test (file_buf = temp_bitcode ~unbuffered:false m);
- test (file_buf = temp_bitcode ~unbuffered:true m);
- test (file_buf = Bytes.of_string (Llvm.MemoryBuffer.as_string (Llvm_bitwriter.write_bitcode_to_memory_buffer m)))
diff --git a/llvm-c/OCaml/core.ml b/llvm-c/OCaml/core.ml
deleted file mode 100644
index 9f999be..0000000
--- a/llvm-c/OCaml/core.ml
+++ /dev/null
@@ -1,1498 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/core.ml && cp %S/Utils/Testsuite.ml %t/Testsuite.ml
- * RUN: %ocamlc -g -w +A -package llvm.analysis -package llvm.bitwriter -I %t/ -linkpkg %t/Testsuite.ml %t/core.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * RUN: %ocamlopt -g -w +A -package llvm.analysis -package llvm.bitwriter -I %t/ -linkpkg %t/Testsuite.ml %t/core.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * RUN: llvm-dis < %t/bitcode.bc > %t/dis.ll
- * RUN: FileCheck %s < %t/dis.ll
- * Do a second pass for things that shouldn't be anywhere.
- * RUN: FileCheck -check-prefix=CHECK-NOWHERE %s < %t/dis.ll
- * XFAIL: vg_leak
- *)
-
-(* Note: It takes several seconds for ocamlopt to link an executable with
- libLLVMCore.a, so it's better to write a big test than a bunch of
- little ones. *)
-
-open Llvm
-open Llvm_bitwriter
-
-open Testsuite
-let context = global_context ()
-let i1_type = Llvm.i1_type context
-let i8_type = Llvm.i8_type context
-let i16_type = Llvm.i16_type context
-let i32_type = Llvm.i32_type context
-let i64_type = Llvm.i64_type context
-let void_type = Llvm.void_type context
-let float_type = Llvm.float_type context
-let double_type = Llvm.double_type context
-let fp128_type = Llvm.fp128_type context
-
-(*===-- Fixture -----------------------------------------------------------===*)
-
-let filename = Sys.argv.(1)
-let m = create_module context filename
-
-(*===-- Modules ----------------------------------------------------------===*)
-
-let test_modules () =
- insist (module_context m = context)
-
-(*===-- Contained types --------------------------------------------------===*)
-
-let test_contained_types () =
- let ar = struct_type context [| i32_type; i8_type |] in
- insist (i32_type = (Array.get (subtypes ar)) 0);
- insist (i8_type = (Array.get (subtypes ar)) 1);
- insist ([| i32_type; i8_type |] = struct_element_types ar)
-
-(*===-- Pointer types ----------------------------------------------------===*)
-
-let test_pointer_types () =
- insist (TypeKind.Pointer = classify_type (pointer_type context));
- insist (0 = address_space (pointer_type context));
- insist (0 = address_space (qualified_pointer_type context 0));
- insist (1 = address_space (qualified_pointer_type context 1))
-
-(*===-- Other types ------------------------------------------------------===*)
-
-let test_other_types () =
- insist (TypeKind.Void = classify_type void_type);
- insist (TypeKind.Label = classify_type (label_type context));
- insist (TypeKind.X86_amx = classify_type (x86_amx_type context));
- insist (TypeKind.Token = classify_type (token_type context));
- insist (TypeKind.Metadata = classify_type (metadata_type context))
-
-(*===-- Conversion --------------------------------------------------------===*)
-
-let test_conversion () =
- insist ("i32" = (string_of_lltype i32_type));
- let c = const_int i32_type 42 in
- insist ("i32 42" = (string_of_llvalue c))
-
-
-(*===-- Target ------------------------------------------------------------===*)
-
-let test_target () =
- begin group "triple";
- let trip = "i686-apple-darwin8" in
- set_target_triple trip m;
- insist (trip = target_triple m)
- end;
-
- begin group "layout";
- let layout = "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:128-n8:16:32-S128" in
- set_data_layout layout m;
- insist (layout = data_layout m)
- end
- (* CHECK: target datalayout = "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:128-n8:16:32-S128"
- * CHECK: target triple = "i686-apple-darwin8"
- *)
-
-
-(*===-- Constants ---------------------------------------------------------===*)
-
-let test_constants () =
- (* CHECK: const_int{{.*}}i32{{.*}}-1
- *)
- group "int";
- let c = const_int i32_type (-1) in
- ignore (define_global "const_int" c m);
- insist (i32_type = type_of c);
- insist (is_constant c);
- insist (Some (-1L) = int64_of_const c);
-
- (* CHECK: const_sext_int{{.*}}i64{{.*}}-1
- *)
- group "sext int";
- let c = const_int i64_type (-1) in
- ignore (define_global "const_sext_int" c m);
- insist (i64_type = type_of c);
- insist (Some (-1L) = int64_of_const c);
-
- (* CHECK: const_zext_int64{{.*}}i64{{.*}}4294967295
- *)
- group "zext int64";
- let c = const_of_int64 i64_type (Int64.of_string "4294967295") false in
- ignore (define_global "const_zext_int64" c m);
- insist (i64_type = type_of c);
- insist (Some 4294967295L = int64_of_const c);
-
- (* CHECK: const_int_string{{.*}}i32{{.*}}-1
- *)
- group "int string";
- let c = const_int_of_string i32_type "-1" 10 in
- ignore (define_global "const_int_string" c m);
- insist (i32_type = type_of c);
- insist (None = (string_of_const c));
- insist (None = float_of_const c);
- insist (Some (-1L) = int64_of_const c);
-
- (* CHECK: const_int64{{.*}}i64{{.*}}9223372036854775807
- *)
- group "max int64";
- let c = const_of_int64 i64_type 9223372036854775807L true in
- ignore (define_global "const_int64" c m) ;
- insist (i64_type = type_of c);
- insist (Some 9223372036854775807L = int64_of_const c);
-
- if Sys.word_size = 64; then begin
- group "long int";
- let c = const_int i64_type (1 lsl 61) in
- insist (c = const_of_int64 i64_type (Int64.of_int (1 lsl 61)) false)
- end;
-
- (* CHECK: @const_string = global {{.*}}c"cruel\00world"
- *)
- group "string";
- let c = const_string context "cruel\000world" in
- ignore (define_global "const_string" c m);
- insist ((array_type i8_type 11) = type_of c);
- insist ((Some "cruel\000world") = (string_of_const c));
-
- (* CHECK: const_stringz{{.*}}"hi\00again\00"
- *)
- group "stringz";
- let c = const_stringz context "hi\000again" in
- ignore (define_global "const_stringz" c m);
- insist ((array_type i8_type 9) = type_of c);
-
- (* CHECK: const_single{{.*}}2.75
- * CHECK: const_double{{.*}}3.1459
- * CHECK: const_double_string{{.*}}2
- * CHECK: const_fake_fp128{{.*}}0xL00000000000000004000000000000000
- * CHECK: const_fp128_string{{.*}}0xLF3CB1CCF26FBC178452FB4EC7F91973F
- *)
- begin group "real";
- let cs = const_float float_type 2.75 in
- ignore (define_global "const_single" cs m);
- insist (float_type = type_of cs);
- insist (float_of_const cs = Some 2.75);
-
- let cd = const_float double_type 3.1459 in
- ignore (define_global "const_double" cd m);
- insist (double_type = type_of cd);
- insist (float_of_const cd = Some 3.1459);
-
- let cd = const_float_of_string double_type "2" in
- ignore (define_global "const_double_string" cd m);
- insist (double_type = type_of cd);
- insist (float_of_const cd = Some 2.);
-
- let cd = const_float fp128_type 2. in
- ignore (define_global "const_fake_fp128" cd m);
- insist (fp128_type = type_of cd);
- insist (float_of_const cd = Some 2.);
-
- let cd = const_float_of_string fp128_type "1e400" in
- ignore (define_global "const_fp128_string" cd m);
- insist (fp128_type = type_of cd);
- insist (float_of_const cd = None);
- end;
-
- let one = const_int i16_type 1 in
- let two = const_int i16_type 2 in
- let three = const_int i32_type 3 in
- let four = const_int i32_type 4 in
-
- (* CHECK: const_array{{.*}}[i32 3, i32 4]
- *)
- group "array";
- let c = const_array i32_type [| three; four |] in
- ignore (define_global "const_array" c m);
- insist ((array_type i32_type 2) = (type_of c));
- insist (element_type (type_of c) = i32_type);
- insist (Some three = (aggregate_element c 0));
- insist (Some four = (aggregate_element c 1));
- insist (None = (aggregate_element c 2));
-
- (* CHECK: const_vector{{.*}}
- *)
- group "vector";
- let c = const_vector [| one; two; one; two;
- one; two; one; two |] in
- ignore (define_global "const_vector" c m);
- insist ((vector_type i16_type 8) = (type_of c));
- insist (element_type (type_of c) = i16_type);
-
- (* CHECK: const_structure{{.*.}}i16 1, i16 2, i32 3, i32 4
- *)
- group "structure";
- let c = const_struct context [| one; two; three; four |] in
- ignore (define_global "const_structure" c m);
- insist ((struct_type context [| i16_type; i16_type; i32_type; i32_type |])
- = (type_of c));
-
- (* CHECK: const_null{{.*}}zeroinit
- *)
- group "null";
- let c = const_null (packed_struct_type context [| i1_type; i8_type; i64_type;
- double_type |]) in
- ignore (define_global "const_null" c m);
-
- (* CHECK: const_all_ones{{.*}}-1
- *)
- group "all ones";
- let c = const_all_ones i64_type in
- ignore (define_global "const_all_ones" c m);
-
- group "pointer null"; begin
- (* CHECK: const_pointer_null = global ptr null
- *)
- let c = const_pointer_null (pointer_type context) in
- ignore (define_global "const_pointer_null" c m);
- end;
-
- (* CHECK: const_undef{{.*}}undef
- *)
- group "undef";
- let c = undef i1_type in
- ignore (define_global "const_undef" c m);
- insist (i1_type = type_of c);
- insist (is_undef c);
-
- (* CHECK: const_poison{{.*}}poison
- *)
- group "poison";
- let c = poison i1_type in
- ignore (define_global "const_poison" c m);
- insist (i1_type = type_of c);
- insist (is_poison c);
-
- group "constant arithmetic";
- (* CHECK: @const_neg = global i64 sub
- * CHECK: @const_nsw_neg = global i64 sub nsw
- * CHECK: @const_nuw_neg = global i64 sub
- * CHECK: @const_not = global i64 xor
- * CHECK: @const_add = global i64 add
- * CHECK: @const_nsw_add = global i64 add nsw
- * CHECK: @const_nuw_add = global i64 add nuw
- * CHECK: @const_sub = global i64 sub
- * CHECK: @const_nsw_sub = global i64 sub nsw
- * CHECK: @const_nuw_sub = global i64 sub nuw
- * CHECK: @const_xor = global i64 xor
- *)
- let void_ptr = pointer_type context in
- let five = const_int i64_type 5 in
- let foldbomb_gv = define_global "FoldBomb" (const_null i8_type) m in
- let foldbomb = const_ptrtoint foldbomb_gv i64_type in
- ignore (define_global "const_neg" (const_neg foldbomb) m);
- ignore (define_global "const_nsw_neg" (const_nsw_neg foldbomb) m);
- ignore (define_global "const_nuw_neg" (const_nuw_neg foldbomb) m);
- ignore (define_global "const_not" (const_not foldbomb) m);
- ignore (define_global "const_add" (const_add foldbomb five) m);
- ignore (define_global "const_nsw_add" (const_nsw_add foldbomb five) m);
- ignore (define_global "const_nuw_add" (const_nuw_add foldbomb five) m);
- ignore (define_global "const_sub" (const_sub foldbomb five) m);
- ignore (define_global "const_nsw_sub" (const_nsw_sub foldbomb five) m);
- ignore (define_global "const_nuw_sub" (const_nuw_sub foldbomb five) m);
- ignore (define_global "const_xor" (const_xor foldbomb five) m);
-
- group "constant casts";
- (* CHECK: const_trunc{{.*}}trunc
- * CHECK: const_ptrtoint{{.*}}ptrtoint
- * CHECK: const_inttoptr{{.*}}inttoptr
- * CHECK: const_bitcast{{.*}}bitcast
- *)
- let i128_type = integer_type context 128 in
- ignore (define_global "const_trunc" (const_trunc (const_add foldbomb five)
- i8_type) m);
- ignore (define_global "const_ptrtoint" (const_ptrtoint
- (const_gep i8_type (const_null (pointer_type context))
- [| const_int i32_type 1 |])
- i32_type) m);
- ignore (define_global "const_inttoptr" (const_inttoptr (const_add foldbomb five)
- void_ptr) m);
- ignore (define_global "const_bitcast" (const_bitcast foldbomb double_type) m);
-
- group "misc constants";
- (* CHECK: const_size_of{{.*}}getelementptr{{.*}}null
- * CHECK: const_gep{{.*}}getelementptr
- * CHECK: const_extractelement{{.*}}extractelement
- * CHECK: const_insertelement{{.*}}insertelement
- * CHECK: const_shufflevector = global <4 x i32>
- *)
- ignore (define_global "const_size_of" (size_of (pointer_type context)) m);
- ignore (define_global "const_gep" (const_gep i8_type foldbomb_gv [| five |])
- m);
- let zero = const_int i32_type 0 in
- let one = const_int i32_type 1 in
- ignore (define_global "const_extractelement" (const_extractelement
- (const_vector [| zero; one; zero; one |])
- (const_trunc foldbomb i32_type)) m);
- ignore (define_global "const_insertelement" (const_insertelement
- (const_vector [| zero; one; zero; one |])
- zero (const_trunc foldbomb i32_type)) m);
- ignore (define_global "const_shufflevector" (const_shufflevector
- (const_vector [| zero; one |])
- (const_vector [| one; zero |])
- (const_vector [| const_int i32_type 0; const_int i32_type 1;
- const_int i32_type 2; const_int i32_type 3 |])) m);
-
- group "asm"; begin
- let ft = function_type void_type [| i32_type; i32_type; i32_type |] in
- ignore (const_inline_asm
- ft
- ""
- "{cx},{ax},{di},~{dirflag},~{fpsr},~{flags},~{edi},~{ecx}"
- true
- false)
- end;
-
- group "recursive struct"; begin
- let nsty = named_struct_type context "rec" in
- let pty = pointer_type context in
- struct_set_body nsty [| i32_type; pty |] false;
- let elts = [| const_int i32_type 4; const_pointer_null pty |] in
- let grec_init = const_named_struct nsty elts in
- ignore (define_global "grec" grec_init m);
- ignore (string_of_lltype nsty);
- end
-
-
-(*===-- Attributes --------------------------------------------------------===*)
-
-let test_attributes () =
- group "enum attrs";
- let nonnull_kind = enum_attr_kind "nonnull" in
- let dereferenceable_kind = enum_attr_kind "dereferenceable" in
- insist (nonnull_kind = (enum_attr_kind "nonnull"));
- insist (nonnull_kind <> dereferenceable_kind);
-
- let nonnull =
- create_enum_attr context "nonnull" 0L in
- let dereferenceable_4 =
- create_enum_attr context "dereferenceable" 4L in
- let dereferenceable_8 =
- create_enum_attr context "dereferenceable" 8L in
- insist (nonnull <> dereferenceable_4);
- insist (dereferenceable_4 <> dereferenceable_8);
- insist (nonnull = (create_enum_attr context "nonnull" 0L));
- insist ((repr_of_attr nonnull) =
- AttrRepr.Enum(nonnull_kind, 0L));
- insist ((repr_of_attr dereferenceable_4) =
- AttrRepr.Enum(dereferenceable_kind, 4L));
- insist ((attr_of_repr context (repr_of_attr nonnull)) =
- nonnull);
- insist ((attr_of_repr context (repr_of_attr dereferenceable_4)) =
- dereferenceable_4);
-
- group "string attrs";
- let foo_bar = create_string_attr context "foo" "bar" in
- let foo_baz = create_string_attr context "foo" "baz" in
- insist (foo_bar <> foo_baz);
- insist (foo_bar = (create_string_attr context "foo" "bar"));
- insist ((repr_of_attr foo_bar) = AttrRepr.String("foo", "bar"));
- insist ((attr_of_repr context (repr_of_attr foo_bar)) = foo_bar);
- ()
-
-(*===-- Global Values -----------------------------------------------------===*)
-
-let test_global_values () =
- let (++) x f = f x; x in
- let zero32 = const_null i32_type in
-
- (* CHECK: GVal01
- *)
- group "naming";
- let g = define_global "TEMPORARY" zero32 m in
- insist ("TEMPORARY" = value_name g);
- set_value_name "GVal01" g;
- insist ("GVal01" = value_name g);
-
- (* CHECK: GVal02{{.*}}linkonce
- *)
- group "linkage";
- let g = define_global "GVal02" zero32 m ++
- set_linkage Linkage.Link_once in
- insist (Linkage.Link_once = linkage g);
-
- (* CHECK: GVal03{{.*}}Hanalei
- *)
- group "section";
- let g = define_global "GVal03" zero32 m ++
- set_section "Hanalei" in
- insist ("Hanalei" = section g);
-
- (* CHECK: GVal04{{.*}}hidden
- *)
- group "visibility";
- let g = define_global "GVal04" zero32 m ++
- set_visibility Visibility.Hidden in
- insist (Visibility.Hidden = visibility g);
-
- (* CHECK: GVal05{{.*}}align 128
- *)
- group "alignment";
- let g = define_global "GVal05" zero32 m ++
- set_alignment 128 in
- insist (128 = alignment g);
-
- (* CHECK: GVal06{{.*}}dllexport
- *)
- group "dll_storage_class";
- let g = define_global "GVal06" zero32 m ++
- set_dll_storage_class DLLStorageClass.DLLExport in
- insist (DLLStorageClass.DLLExport = dll_storage_class g);
-
- (* CHECK: GVal07{{.*}}!test !0
- * See metadata check at the end of the file.
- *)
- group "metadata";
- let g = define_global "GVal07" zero32 m in
- let md_string = mdstring context "global test metadata" in
- let md_node = mdnode context [| zero32; md_string |] |> value_as_metadata in
- let mdkind_test = mdkind_id context "test" in
- global_set_metadata g mdkind_test md_node;
- let md' = global_copy_all_metadata g in
- insist (md' = [| mdkind_test, md_node |])
-
-(*===-- Global Variables --------------------------------------------------===*)
-
-let test_global_variables () =
- let (++) x f = f x; x in
- let forty_two32 = const_int i32_type 42 in
-
- group "declarations"; begin
- (* CHECK: @GVar01 = external global i32
- * CHECK: @QGVar01 = external addrspace(3) global i32
- *)
- insist (None == lookup_global "GVar01" m);
- let g = declare_global i32_type "GVar01" m in
- insist (is_declaration g);
- insist (pointer_type context ==
- type_of (declare_global float_type "GVar01" m));
- insist (g == declare_global i32_type "GVar01" m);
- insist (match lookup_global "GVar01" m with Some x -> x = g
- | None -> false);
-
- insist (None == lookup_global "QGVar01" m);
- let g = declare_qualified_global i32_type "QGVar01" 3 m in
- insist (is_declaration g);
- insist (qualified_pointer_type context 3 ==
- type_of (declare_qualified_global float_type "QGVar01" 3 m));
- insist (g == declare_qualified_global i32_type "QGVar01" 3 m);
- insist (match lookup_global "QGVar01" m with Some x -> x = g
- | None -> false);
- end;
-
- group "definitions"; begin
- (* CHECK: @GVar02 = global i32 42
- * CHECK: @GVar03 = global i32 42
- * CHECK: @QGVar02 = addrspace(3) global i32 42
- * CHECK: @QGVar03 = addrspace(3) global i32 42
- *)
- let g = define_global "GVar02" forty_two32 m in
- let g2 = declare_global i32_type "GVar03" m ++
- set_initializer forty_two32 in
- insist (not (is_declaration g));
- insist (not (is_declaration g2));
- insist ((global_initializer g) = (global_initializer g2));
-
- let g = define_qualified_global "QGVar02" forty_two32 3 m in
- let g2 = declare_qualified_global i32_type "QGVar03" 3 m ++
- set_initializer forty_two32 in
- insist (not (is_declaration g));
- insist (not (is_declaration g2));
- insist ((global_initializer g) = (global_initializer g2));
- end;
-
- (* CHECK: GVar04{{.*}}thread_local
- *)
- group "threadlocal";
- let g = define_global "GVar04" forty_two32 m ++
- set_thread_local true in
- insist (is_thread_local g);
-
- (* CHECK: GVar05{{.*}}thread_local(initialexec)
- *)
- group "threadlocal_mode";
- let g = define_global "GVar05" forty_two32 m ++
- set_thread_local_mode ThreadLocalMode.InitialExec in
- insist ((thread_local_mode g) = ThreadLocalMode.InitialExec);
-
- (* CHECK: GVar06{{.*}}externally_initialized
- *)
- group "externally_initialized";
- let g = define_global "GVar06" forty_two32 m ++
- set_externally_initialized true in
- insist (is_externally_initialized g);
-
- (* CHECK-NOWHERE-NOT: GVar07
- *)
- group "delete";
- let g = define_global "GVar07" forty_two32 m in
- delete_global g;
-
- (* CHECK: ConstGlobalVar{{.*}}constant
- *)
- group "constant";
- let g = define_global "ConstGlobalVar" forty_two32 m in
- insist (not (is_global_constant g));
- set_global_constant true g;
- insist (is_global_constant g);
-
- begin group "iteration";
- let m = create_module context "temp" in
-
- insist (get_module_identifier m = "temp");
- set_module_identifer m "temp2";
- insist (get_module_identifier m = "temp2");
-
- insist (At_end m = global_begin m);
- insist (At_start m = global_end m);
-
- let g1 = declare_global i32_type "One" m in
- let g2 = declare_global i32_type "Two" m in
-
- insist (Before g1 = global_begin m);
- insist (Before g2 = global_succ g1);
- insist (At_end m = global_succ g2);
-
- insist (After g2 = global_end m);
- insist (After g1 = global_pred g2);
- insist (At_start m = global_pred g1);
-
- let lf s x = s ^ "->" ^ value_name x in
- insist ("->One->Two" = fold_left_globals lf "" m);
-
- let rf x s = value_name x ^ "<-" ^ s in
- insist ("One<-Two<-" = fold_right_globals rf m "");
-
- dispose_module m
- end
-
-(* String globals built below are emitted here.
- * CHECK: build_global_string{{.*}}stringval
- *)
-
-
-(*===-- Uses --------------------------------------------------------------===*)
-
-let test_uses () =
- let ty = function_type i32_type [| i32_type; i32_type |] in
- let fn = define_function "use_function" ty m in
- let b = builder_at_end context (entry_block fn) in
-
- let p1 = param fn 0 in
- let p2 = param fn 1 in
- let v1 = build_add p1 p2 "v1" b in
- let v2 = build_add p1 v1 "v2" b in
- let _ = build_add v1 v2 "v3" b in
-
- let lf s u = value_name (user u) ^ "->" ^ s in
- insist ("v2->v3->" = fold_left_uses lf "" v1);
- let rf u s = value_name (user u) ^ "<-" ^ s in
- insist ("v3<-v2<-" = fold_right_uses rf v1 "");
-
- let lf s u = value_name (used_value u) ^ "->" ^ s in
- insist ("v1->v1->" = fold_left_uses lf "" v1);
-
- let rf u s = value_name (used_value u) ^ "<-" ^ s in
- insist ("v1<-v1<-" = fold_right_uses rf v1 "");
-
- ignore (build_unreachable b)
-
-
-(*===-- Users -------------------------------------------------------------===*)
-
-let test_users () =
- let ty = function_type i32_type [| i32_type; i32_type |] in
- let fn = define_function "user_function" ty m in
- let b = builder_at_end context (entry_block fn) in
-
- let p1 = param fn 0 in
- let p2 = param fn 1 in
- let a3 = build_alloca i32_type "user_alloca" b in
- let p3 = build_load i32_type a3 "user_load" b in
- let i = build_add p1 p2 "sum" b in
-
- insist ((num_operands i) = 2);
- insist ((operand i 0) = p1);
- insist ((operand i 1) = p2);
-
- set_operand i 1 p3;
- insist ((operand i 1) != p2);
- insist ((operand i 1) = p3);
-
- ignore (build_unreachable b)
-
-
-(*===-- Aliases -----------------------------------------------------------===*)
-
-let test_aliases () =
- (* CHECK: @alias = alias i32, ptr @aliasee
- *)
- let forty_two32 = const_int i32_type 42 in
- let v = define_global "aliasee" forty_two32 m in
- ignore (add_alias m i32_type 0 v "alias")
-
-
-(*===-- Functions ---------------------------------------------------------===*)
-
-let test_functions () =
- let ty = function_type i32_type [| i32_type; i64_type |] in
- let ty2 = function_type i8_type [| i8_type; i64_type |] in
-
- (* CHECK: declare i32 @Fn1(i32, i64)
- *)
- begin group "declare";
- insist (None = lookup_function "Fn1" m);
- let fn = declare_function "Fn1" ty m in
- insist (pointer_type context = type_of fn);
- insist (is_declaration fn);
- insist (0 = Array.length (basic_blocks fn));
- insist (pointer_type context == type_of (declare_function "Fn1" ty2 m));
- insist (fn == declare_function "Fn1" ty m);
- insist (None <> lookup_function "Fn1" m);
- insist (match lookup_function "Fn1" m with Some x -> x = fn
- | None -> false);
- insist (m == global_parent fn)
- end;
-
- (* CHECK-NOWHERE-NOT: Fn2
- *)
- group "delete";
- let fn = declare_function "Fn2" ty m in
- delete_function fn;
-
- (* CHECK: define{{.*}}Fn3
- *)
- group "define";
- let fn = define_function "Fn3" ty m in
- insist (not (is_declaration fn));
- insist (1 = Array.length (basic_blocks fn));
- ignore (build_unreachable (builder_at_end context (entry_block fn)));
-
- (* CHECK: define{{.*}}Fn4{{.*}}Param1{{.*}}Param2
- *)
- group "params";
- let fn = define_function "Fn4" ty m in
- let params = params fn in
- insist (2 = Array.length params);
- insist (params.(0) = param fn 0);
- insist (params.(1) = param fn 1);
- insist (i32_type = type_of params.(0));
- insist (i64_type = type_of params.(1));
- set_value_name "Param1" params.(0);
- set_value_name "Param2" params.(1);
- ignore (build_unreachable (builder_at_end context (entry_block fn)));
-
- (* CHECK: fastcc{{.*}}Fn5
- *)
- group "callconv";
- let fn = define_function "Fn5" ty m in
- insist (CallConv.c = function_call_conv fn);
- set_function_call_conv CallConv.fast fn;
- insist (CallConv.fast = function_call_conv fn);
- ignore (build_unreachable (builder_at_end context (entry_block fn)));
-
- begin group "gc";
- (* CHECK: Fn6{{.*}}gc{{.*}}shadowstack
- *)
- let fn = define_function "Fn6" ty m in
- insist (None = gc fn);
- set_gc (Some "ocaml") fn;
- insist (Some "ocaml" = gc fn);
- set_gc None fn;
- insist (None = gc fn);
- set_gc (Some "shadowstack") fn;
- ignore (build_unreachable (builder_at_end context (entry_block fn)));
- end;
-
- begin group "iteration";
- let m = create_module context "temp" in
-
- insist (At_end m = function_begin m);
- insist (At_start m = function_end m);
-
- let f1 = define_function "One" ty m in
- let f2 = define_function "Two" ty m in
-
- insist (Before f1 = function_begin m);
- insist (Before f2 = function_succ f1);
- insist (At_end m = function_succ f2);
-
- insist (After f2 = function_end m);
- insist (After f1 = function_pred f2);
- insist (At_start m = function_pred f1);
-
- let lf s x = s ^ "->" ^ value_name x in
- insist ("->One->Two" = fold_left_functions lf "" m);
-
- let rf x s = value_name x ^ "<-" ^ s in
- insist ("One<-Two<-" = fold_right_functions rf m "");
-
- dispose_module m
- end
-
-
-(*===-- Params ------------------------------------------------------------===*)
-
-let test_params () =
- begin group "iteration";
- let m = create_module context "temp" in
-
- let vf = define_function "void" (function_type void_type [| |]) m in
-
- insist (At_end vf = param_begin vf);
- insist (At_start vf = param_end vf);
-
- let ty = function_type void_type [| i32_type; i32_type |] in
- let f = define_function "f" ty m in
- let p1 = param f 0 in
- let p2 = param f 1 in
- set_value_name "One" p1;
- set_value_name "Two" p2;
-
- insist (Before p1 = param_begin f);
- insist (Before p2 = param_succ p1);
- insist (At_end f = param_succ p2);
-
- insist (After p2 = param_end f);
- insist (After p1 = param_pred p2);
- insist (At_start f = param_pred p1);
-
- let lf s x = s ^ "->" ^ value_name x in
- insist ("->One->Two" = fold_left_params lf "" f);
-
- let rf x s = value_name x ^ "<-" ^ s in
- insist ("One<-Two<-" = fold_right_params rf f "");
-
- dispose_module m
- end
-
-
-(*===-- Basic Blocks ------------------------------------------------------===*)
-
-let test_basic_blocks () =
- let ty = function_type void_type [| |] in
-
- (* CHECK: Bb1
- *)
- group "entry";
- let fn = declare_function "X" ty m in
- let bb = append_block context "Bb1" fn in
- insist (bb = entry_block fn);
- ignore (build_unreachable (builder_at_end context bb));
-
- (* CHECK-NOWHERE-NOT: Bb2
- *)
- group "delete";
- let fn = declare_function "X2" ty m in
- let bb = append_block context "Bb2" fn in
- delete_block bb;
-
- group "insert";
- let fn = declare_function "X3" ty m in
- let bbb = append_block context "b" fn in
- let bba = insert_block context "a" bbb in
- insist ([| bba; bbb |] = basic_blocks fn);
- ignore (build_unreachable (builder_at_end context bba));
- ignore (build_unreachable (builder_at_end context bbb));
-
- (* CHECK: Bb3
- *)
- group "name/value";
- let fn = define_function "X4" ty m in
- let bb = entry_block fn in
- ignore (build_unreachable (builder_at_end context bb));
- let bbv = value_of_block bb in
- set_value_name "Bb3" bbv;
- insist ("Bb3" = value_name bbv);
-
- group "casts";
- let fn = define_function "X5" ty m in
- let bb = entry_block fn in
- ignore (build_unreachable (builder_at_end context bb));
- insist (bb = block_of_value (value_of_block bb));
- insist (value_is_block (value_of_block bb));
- insist (not (value_is_block (const_null i32_type)));
-
- begin group "iteration";
- let m = create_module context "temp" in
- let f = declare_function "Temp" (function_type i32_type [| |]) m in
-
- insist (At_end f = block_begin f);
- insist (At_start f = block_end f);
-
- let b1 = append_block context "One" f in
- let b2 = append_block context "Two" f in
-
- insist (Before b1 = block_begin f);
- insist (Before b2 = block_succ b1);
- insist (At_end f = block_succ b2);
-
- insist (After b2 = block_end f);
- insist (After b1 = block_pred b2);
- insist (At_start f = block_pred b1);
-
- let lf s x = s ^ "->" ^ value_name (value_of_block x) in
- insist ("->One->Two" = fold_left_blocks lf "" f);
-
- let rf x s = value_name (value_of_block x) ^ "<-" ^ s in
- insist ("One<-Two<-" = fold_right_blocks rf f "");
-
- dispose_module m
- end
-
-
-(*===-- Instructions ------------------------------------------------------===*)
-
-let test_instructions () =
- begin group "iteration";
- let m = create_module context "temp" in
- let fty = function_type void_type [| i32_type; i32_type |] in
- let f = define_function "f" fty m in
- let bb = entry_block f in
- let b = builder_at context (At_end bb) in
-
- insist (At_end bb = instr_begin bb);
- insist (At_start bb = instr_end bb);
-
- let i1 = build_add (param f 0) (param f 1) "One" b in
- let i2 = build_sub (param f 0) (param f 1) "Two" b in
-
- insist (Before i1 = instr_begin bb);
- insist (Before i2 = instr_succ i1);
- insist (At_end bb = instr_succ i2);
-
- insist (After i2 = instr_end bb);
- insist (After i1 = instr_pred i2);
- insist (At_start bb = instr_pred i1);
-
- let lf s x = s ^ "->" ^ value_name x in
- insist ("->One->Two" = fold_left_instrs lf "" bb);
-
- let rf x s = value_name x ^ "<-" ^ s in
- insist ("One<-Two<-" = fold_right_instrs rf bb "");
-
- dispose_module m
- end;
-
- group "clone instr";
- begin
- (* CHECK: %clone = add i32 %0, 2
- *)
- let fty = function_type void_type [| i32_type |] in
- let fn = define_function "BuilderParent" fty m in
- let bb = entry_block fn in
- let b = builder_at_end context bb in
- let p = param fn 0 in
- let sum = build_add p p "sum" b in
- let y = const_int i32_type 2 in
- let clone = instr_clone sum in
- set_operand clone 0 p;
- set_operand clone 1 y;
- insert_into_builder clone "clone" b;
- ignore (build_ret_void b)
- end
-
-
-(*===-- Builder -----------------------------------------------------------===*)
-
-let test_builder () =
- let (++) x f = f x; x in
-
- begin group "parent";
- insist (try
- ignore (insertion_block (builder context));
- false
- with Not_found ->
- true);
-
- let fty = function_type void_type [| i32_type |] in
- let fn = define_function "BuilderParent" fty m in
- let bb = entry_block fn in
- let b = builder_at_end context bb in
- let p = param fn 0 in
- let sum = build_add p p "sum" b in
- ignore (build_ret_void b);
-
- insist (fn = block_parent bb);
- insist (fn = param_parent p);
- insist (bb = instr_parent sum);
- insist (bb = insertion_block b)
- end;
-
- group "ret void";
- begin
- (* CHECK: ret void
- *)
- let fty = function_type void_type [| |] in
- let fn = declare_function "X6" fty m in
- let b = builder_at_end context (append_block context "Bb01" fn) in
- ignore (build_ret_void b)
- end;
-
- group "ret aggregate";
- begin
- (* CHECK: ret { i8, i64 } { i8 4, i64 5 }
- *)
- let sty = struct_type context [| i8_type; i64_type |] in
- let fty = function_type sty [| |] in
- let fn = declare_function "XA6" fty m in
- let b = builder_at_end context (append_block context "Bb01" fn) in
- let agg = [| const_int i8_type 4; const_int i64_type 5 |] in
- ignore (build_aggregate_ret agg b)
- end;
-
- (* The rest of the tests will use one big function. *)
- let fty = function_type i32_type [| i32_type; i32_type |] in
- let fn = define_function "X7" fty m in
- let atentry = builder_at_end context (entry_block fn) in
- let p1 = param fn 0 ++ set_value_name "P1" in
- let p2 = param fn 1 ++ set_value_name "P2" in
- let f1 = build_uitofp p1 float_type "F1" atentry in
- let f2 = build_uitofp p2 float_type "F2" atentry in
-
- let bb00 = append_block context "Bb00" fn in
- ignore (build_unreachable (builder_at_end context bb00));
-
- group "function attribute";
- begin
- let signext = create_enum_attr context "signext" 0L in
- let zeroext = create_enum_attr context "zeroext" 0L in
- let noalias = create_enum_attr context "noalias" 0L in
- let nounwind = create_enum_attr context "nounwind" 0L in
- let no_sse = create_string_attr context "no-sse" "" in
-
- add_function_attr fn signext (AttrIndex.Param 0);
- add_function_attr fn noalias (AttrIndex.Param 1);
- insist ((function_attrs fn (AttrIndex.Param 1)) = [|noalias|]);
- remove_enum_function_attr fn (enum_attr_kind "noalias") (AttrIndex.Param 1);
- add_function_attr fn no_sse (AttrIndex.Param 1);
- insist ((function_attrs fn (AttrIndex.Param 1)) = [|no_sse|]);
- remove_string_function_attr fn "no-sse" (AttrIndex.Param 1);
- insist ((function_attrs fn (AttrIndex.Param 1)) = [||]);
- add_function_attr fn nounwind AttrIndex.Function;
- add_function_attr fn zeroext AttrIndex.Return;
-
- (* CHECK: define zeroext i32 @X7(i32 signext %P1, i32 %P2)
- *)
- end;
-
- group "casts"; begin
- let void_ptr = pointer_type context in
-
- (* CHECK-DAG: %build_trunc = trunc i32 %P1 to i8
- * CHECK-DAG: %build_trunc2 = trunc i32 %P1 to i8
- * CHECK-DAG: %build_trunc3 = trunc i32 %P1 to i8
- * CHECK-DAG: %build_zext = zext i8 %build_trunc to i32
- * CHECK-DAG: %build_zext2 = zext i8 %build_trunc to i32
- * CHECK-DAG: %build_sext = sext i32 %build_zext to i64
- * CHECK-DAG: %build_sext2 = sext i32 %build_zext to i64
- * CHECK-DAG: %build_sext3 = sext i32 %build_zext to i64
- * CHECK-DAG: %build_uitofp = uitofp i64 %build_sext to float
- * CHECK-DAG: %build_sitofp = sitofp i32 %build_zext to double
- * CHECK-DAG: %build_fptoui = fptoui float %build_uitofp to i32
- * CHECK-DAG: %build_fptosi = fptosi double %build_sitofp to i64
- * CHECK-DAG: %build_fptrunc = fptrunc double %build_sitofp to float
- * CHECK-DAG: %build_fptrunc2 = fptrunc double %build_sitofp to float
- * CHECK-DAG: %build_fpext = fpext float %build_fptrunc to double
- * CHECK-DAG: %build_fpext2 = fpext float %build_fptrunc to double
- * CHECK-DAG: %build_inttoptr = inttoptr i32 %P1 to ptr
- * CHECK-DAG: %build_ptrtoint = ptrtoint ptr %build_inttoptr to i64
- * CHECK-DAG: %build_ptrtoint2 = ptrtoint ptr %build_inttoptr to i64
- * CHECK-DAG: %build_bitcast = bitcast i64 %build_ptrtoint to double
- * CHECK-DAG: %build_bitcast2 = bitcast i64 %build_ptrtoint to double
- * CHECK-DAG: %build_bitcast3 = bitcast i64 %build_ptrtoint to double
- * CHECK-DAG: %build_bitcast4 = bitcast i64 %build_ptrtoint to double
- *)
- let inst28 = build_trunc p1 i8_type "build_trunc" atentry in
- let inst29 = build_zext inst28 i32_type "build_zext" atentry in
- let inst30 = build_sext inst29 i64_type "build_sext" atentry in
- let inst31 = build_uitofp inst30 float_type "build_uitofp" atentry in
- let inst32 = build_sitofp inst29 double_type "build_sitofp" atentry in
- ignore(build_fptoui inst31 i32_type "build_fptoui" atentry);
- ignore(build_fptosi inst32 i64_type "build_fptosi" atentry);
- let inst35 = build_fptrunc inst32 float_type "build_fptrunc" atentry in
- ignore(build_fpext inst35 double_type "build_fpext" atentry);
- let inst37 = build_inttoptr p1 void_ptr "build_inttoptr" atentry in
- let inst38 = build_ptrtoint inst37 i64_type "build_ptrtoint" atentry in
- ignore(build_bitcast inst38 double_type "build_bitcast" atentry);
- ignore(build_zext_or_bitcast inst38 double_type "build_bitcast2" atentry);
- ignore(build_sext_or_bitcast inst38 double_type "build_bitcast3" atentry);
- ignore(build_trunc_or_bitcast inst38 double_type "build_bitcast4" atentry);
- ignore(build_pointercast inst37 (pointer_type context) "build_pointercast" atentry);
-
- ignore(build_zext_or_bitcast inst28 i32_type "build_zext2" atentry);
- ignore(build_sext_or_bitcast inst29 i64_type "build_sext2" atentry);
- ignore(build_trunc_or_bitcast p1 i8_type "build_trunc2" atentry);
- ignore(build_pointercast inst37 i64_type "build_ptrtoint2" atentry);
- ignore(build_intcast inst29 i64_type "build_sext3" atentry);
- ignore(build_intcast p1 i8_type "build_trunc3" atentry);
- ignore(build_fpcast inst35 double_type "build_fpext2" atentry);
- ignore(build_fpcast inst32 float_type "build_fptrunc2" atentry);
- end;
-
- group "comparisons"; begin
- (* CHECK: %build_icmp_ne = icmp ne i32 %P1, %P2
- * CHECK: %build_icmp_sle = icmp sle i32 %P2, %P1
- * CHECK: %build_fcmp_false = fcmp false float %F1, %F2
- * CHECK: %build_fcmp_true = fcmp true float %F2, %F1
- * CHECK: %build_is_null{{.*}}= icmp eq{{.*}}%X0,{{.*}}null
- * CHECK: %build_is_not_null = icmp ne ptr %X1, null
- * CHECK: %build_ptrdiff
- *)
- let c = build_icmp Icmp.Ne p1 p2 "build_icmp_ne" atentry in
- insist (Some Icmp.Ne = icmp_predicate c);
- insist (None = fcmp_predicate c);
-
- let c = build_icmp Icmp.Sle p2 p1 "build_icmp_sle" atentry in
- insist (Some Icmp.Sle = icmp_predicate c);
- insist (None = fcmp_predicate c);
-
- let c = build_fcmp Fcmp.False f1 f2 "build_fcmp_false" atentry in
- (* insist (Some Fcmp.False = fcmp_predicate c); *)
- insist (None = icmp_predicate c);
-
- let c = build_fcmp Fcmp.True f2 f1 "build_fcmp_true" atentry in
- (* insist (Some Fcmp.True = fcmp_predicate c); *)
- insist (None = icmp_predicate c);
-
- let g0 = declare_global (pointer_type context) "g0" m in
- let g1 = declare_global (pointer_type context) "g1" m in
- let p0 = build_load (pointer_type context) g0 "X0" atentry in
- let p1 = build_load (pointer_type context) g1 "X1" atentry in
- ignore (build_is_null p0 "build_is_null" atentry);
- ignore (build_is_not_null p1 "build_is_not_null" atentry);
- ignore (build_ptrdiff i8_type p1 p0 "build_ptrdiff" atentry);
- end;
-
- group "miscellaneous"; begin
- (* CHECK: %build_call = tail call cc63 zeroext i32 @{{.*}}(i32 signext %P2, i32 %P1)
- * CHECK: %build_select = select i1 %build_icmp, i32 %P1, i32 %P2
- * CHECK: %build_va_arg = va_arg ptr null, i32
- * CHECK: %build_extractelement = extractelement <4 x i32> %Vec1, i32 %P2
- * CHECK: %build_insertelement = insertelement <4 x i32> %Vec1, i32 %P1, i32 %P2
- * CHECK: %build_shufflevector = shufflevector <4 x i32> %Vec1, <4 x i32> %Vec2, <4 x i32>
- * CHECK: %build_insertvalue0 = insertvalue{{.*}}%bl, i32 1, 0
- * CHECK: %build_extractvalue = extractvalue{{.*}}%build_insertvalue1, 1
- *)
- let ci = build_call fty fn [| p2; p1 |] "build_call" atentry in
- insist (CallConv.c = instruction_call_conv ci);
- set_instruction_call_conv 63 ci;
- insist (63 = instruction_call_conv ci);
- insist (not (is_tail_call ci));
- set_tail_call true ci;
- insist (is_tail_call ci);
-
- let signext = create_enum_attr context "signext" 0L in
- let zeroext = create_enum_attr context "zeroext" 0L in
- let noalias = create_enum_attr context "noalias" 0L in
- let noreturn = create_enum_attr context "noreturn" 0L in
- let no_sse = create_string_attr context "no-sse" "" in
-
- add_call_site_attr ci signext (AttrIndex.Param 0);
- add_call_site_attr ci noalias (AttrIndex.Param 1);
- insist ((call_site_attrs ci (AttrIndex.Param 1)) = [|noalias|]);
- remove_enum_call_site_attr ci (enum_attr_kind "noalias") (AttrIndex.Param 1);
- add_call_site_attr ci no_sse (AttrIndex.Param 1);
- insist ((call_site_attrs ci (AttrIndex.Param 1)) = [|no_sse|]);
- remove_string_call_site_attr ci "no-sse" (AttrIndex.Param 1);
- insist ((call_site_attrs ci (AttrIndex.Param 1)) = [||]);
- add_call_site_attr ci noreturn AttrIndex.Function;
- add_call_site_attr ci zeroext AttrIndex.Return;
-
- let inst46 = build_icmp Icmp.Eq p1 p2 "build_icmp" atentry in
- ignore (build_select inst46 p1 p2 "build_select" atentry);
- ignore (build_va_arg
- (const_null (pointer_type context))
- i32_type "build_va_arg" atentry);
-
- (* Set up some vector vregs. *)
- let one = const_int i32_type 1 in
- let zero = const_int i32_type 0 in
- let t1 = const_vector [| one; zero; one; zero |] in
- let t2 = const_vector [| zero; one; zero; one |] in
- let t3 = const_vector [| one; one; zero; zero |] in
- let vec1 = build_insertelement t1 p1 p2 "Vec1" atentry in
- let vec2 = build_insertelement t2 p1 p2 "Vec2" atentry in
- let sty = struct_type context [| i32_type; i8_type |] in
-
- ignore (build_extractelement vec1 p2 "build_extractelement" atentry);
- ignore (build_insertelement vec1 p1 p2 "build_insertelement" atentry);
- ignore (build_shufflevector vec1 vec2 t3 "build_shufflevector" atentry);
-
- let p = build_alloca sty "ba" atentry in
- let agg = build_load sty p "bl" atentry in
- let agg0 = build_insertvalue agg (const_int i32_type 1) 0
- "build_insertvalue0" atentry in
- let agg1 = build_insertvalue agg0 (const_int i8_type 2) 1
- "build_insertvalue1" atentry in
- ignore (build_extractvalue agg1 1 "build_extractvalue" atentry)
- end;
-
- group "metadata"; begin
- (* CHECK: %metadata = add i32 %P1, %P2, !test !2
- * !2 is metadata emitted at EOF.
- *)
- let i = build_add p1 p2 "metadata" atentry in
- insist ((has_metadata i) = false);
-
- let m1 = const_int i32_type 1 in
- let m2 = mdstring context "metadata test" in
- let md = mdnode context [| m1; m2 |] in
-
- let kind = mdkind_id context "test" in
- set_metadata i kind md;
-
- insist ((has_metadata i) = true);
- insist ((metadata i kind) = Some md);
- insist ((get_mdnode_operands md) = [| m1; m2 |]);
-
- clear_metadata i kind;
-
- insist ((has_metadata i) = false);
- insist ((metadata i kind) = None);
-
- set_metadata i kind md
- end;
-
- group "named metadata"; begin
- (* !llvm.module.flags is emitted at EOF. *)
- let n1 = const_int i32_type 1 in
- let n2 = mdstring context "Debug Info Version" in
- let n3 = const_int i32_type 3 in
- let md = mdnode context [| n1; n2; n3 |] in
- add_named_metadata_operand m "llvm.module.flags" md;
-
- insist ((get_named_metadata m "llvm.module.flags") = [| md |])
- end;
-
- group "ret"; begin
- (* CHECK: ret{{.*}}P1
- *)
- let ret = build_ret p1 atentry in
- position_before_dbg_records ret atentry
- end;
-
- (* see test/Feature/exception.ll *)
- let bblpad = append_block context "Bblpad" fn in
- let rt = struct_type context [| pointer_type context; i32_type |] in
- let ft = var_arg_function_type i32_type [||] in
- let personality = declare_function "__gxx_personality_v0" ft m in
- let ztic = declare_global (pointer_type context) "_ZTIc" m in
- let ztid = declare_global (pointer_type context) "_ZTId" m in
- let ztipkc = declare_global (pointer_type context) "_ZTIPKc" m in
- begin
- set_global_constant true ztic;
- set_global_constant true ztid;
- set_global_constant true ztipkc;
- let lp = build_landingpad rt personality 0 "lpad"
- (builder_at_end context bblpad) in begin
- set_cleanup lp true;
- add_clause lp ztic;
- insist((pointer_type context) = type_of ztid);
- let ety = pointer_type context in
- add_clause lp (const_array ety [| ztipkc; ztid |]);
- ignore (build_resume lp (builder_at_end context bblpad));
- end;
- (* CHECK: landingpad
- * CHECK: cleanup
- * CHECK: catch{{.*}}ptr{{.*}}@_ZTIc
- * CHECK: filter{{.*}}@_ZTIPKc{{.*}}@_ZTId
- * CHECK: resume
- * *)
- end;
-
- group "br"; begin
- (* CHECK: br{{.*}}Bb02
- *)
- let bb02 = append_block context "Bb02" fn in
- let b = builder_at_end context bb02 in
- let br = build_br bb02 b in
- insist (successors br = [| bb02 |]) ;
- insist (is_conditional br = false) ;
- insist (get_branch br = Some (`Unconditional bb02)) ;
- end;
-
- group "cond_br"; begin
- (* CHECK: br{{.*}}build_br{{.*}}Bb03{{.*}}Bb00
- *)
- let bb03 = append_block context "Bb03" fn in
- let b = builder_at_end context bb03 in
- let cond = build_trunc p1 i1_type "build_br" b in
- let br = build_cond_br cond bb03 bb00 b in
- insist (num_successors br = 2) ;
- insist (successor br 0 = bb03) ;
- insist (successor br 1 = bb00) ;
- insist (is_conditional br = true) ;
- insist (get_branch br = Some (`Conditional (cond, bb03, bb00))) ;
- end;
-
- group "switch"; begin
- (* CHECK: switch{{.*}}P1{{.*}}SwiBlock3
- * CHECK: 2,{{.*}}SwiBlock2
- *)
- let bb1 = append_block context "SwiBlock1" fn in
- let bb2 = append_block context "SwiBlock2" fn in
- ignore (build_unreachable (builder_at_end context bb2));
- let bb3 = append_block context "SwiBlock3" fn in
- ignore (build_unreachable (builder_at_end context bb3));
- let si = build_switch p1 bb3 1 (builder_at_end context bb1) in begin
- ignore (add_case si (const_int i32_type 2) bb2);
- insist (switch_default_dest si = bb3);
- end;
- insist (num_successors si = 2) ;
- insist (get_branch si = None) ;
- end;
-
- group "malloc/free"; begin
- (* CHECK: call{{.*}}@malloc(i32 ptrtoint
- * CHECK: call{{.*}}@free(ptr
- * CHECK: call{{.*}}@malloc(i32 %
- *)
- let bb1 = append_block context "MallocBlock1" fn in
- let m1 = (build_malloc (pointer_type context) "m1"
- (builder_at_end context bb1)) in
- ignore (build_free m1 (builder_at_end context bb1));
- ignore (build_array_malloc i32_type p1 "m2" (builder_at_end context bb1));
- ignore (build_unreachable (builder_at_end context bb1));
- end;
-
- group "indirectbr"; begin
- (* CHECK: indirectbr ptr blockaddress(@X7, %IBRBlock2), [label %IBRBlock2, label %IBRBlock3]
- *)
- let bb1 = append_block context "IBRBlock1" fn in
-
- let bb2 = append_block context "IBRBlock2" fn in
- ignore (build_unreachable (builder_at_end context bb2));
-
- let bb3 = append_block context "IBRBlock3" fn in
- ignore (build_unreachable (builder_at_end context bb3));
-
- let addr = block_address fn bb2 in
- let ibr = build_indirect_br addr 2 (builder_at_end context bb1) in
- ignore (add_destination ibr bb2);
- ignore (add_destination ibr bb3)
- end;
-
- group "invoke"; begin
- (* CHECK: build_invoke{{.*}}invoke{{.*}}P1{{.*}}P2
- * CHECK: to{{.*}}Bb04{{.*}}unwind{{.*}}Bblpad
- *)
- let bb04 = append_block context "Bb04" fn in
- let b = builder_at_end context bb04 in
- ignore (build_invoke fty fn [| p1; p2 |] bb04 bblpad "build_invoke" b)
- end;
-
- group "unreachable"; begin
- (* CHECK: unreachable
- *)
- let bb06 = append_block context "Bb06" fn in
- let b = builder_at_end context bb06 in
- ignore (build_unreachable b)
- end;
-
- group "arithmetic"; begin
- let bb07 = append_block context "Bb07" fn in
- let b = builder_at_end context bb07 in
-
- (* CHECK: %build_add = add i32 %P1, %P2
- * CHECK: %build_nsw_add = add nsw i32 %P1, %P2
- * CHECK: %build_nuw_add = add nuw i32 %P1, %P2
- * CHECK: %build_fadd = fadd float %F1, %F2
- * CHECK: %build_sub = sub i32 %P1, %P2
- * CHECK: %build_nsw_sub = sub nsw i32 %P1, %P2
- * CHECK: %build_nuw_sub = sub nuw i32 %P1, %P2
- * CHECK: %build_fsub = fsub float %F1, %F2
- * CHECK: %build_mul = mul i32 %P1, %P2
- * CHECK: %build_nsw_mul = mul nsw i32 %P1, %P2
- * CHECK: %build_nuw_mul = mul nuw i32 %P1, %P2
- * CHECK: %build_fmul = fmul float %F1, %F2
- * CHECK: %build_udiv = udiv i32 %P1, %P2
- * CHECK: %build_sdiv = sdiv i32 %P1, %P2
- * CHECK: %build_exact_sdiv = sdiv exact i32 %P1, %P2
- * CHECK: %build_fdiv = fdiv float %F1, %F2
- * CHECK: %build_urem = urem i32 %P1, %P2
- * CHECK: %build_srem = srem i32 %P1, %P2
- * CHECK: %build_frem = frem float %F1, %F2
- * CHECK: %build_shl = shl i32 %P1, %P2
- * CHECK: %build_lshl = lshr i32 %P1, %P2
- * CHECK: %build_ashl = ashr i32 %P1, %P2
- * CHECK: %build_and = and i32 %P1, %P2
- * CHECK: %build_or = or i32 %P1, %P2
- * CHECK: %build_xor = xor i32 %P1, %P2
- * CHECK: %build_neg = sub i32 0, %P1
- * CHECK: %build_nsw_neg = sub nsw i32 0, %P1
- * CHECK: %build_nuw_neg = sub nuw i32 0, %P1
- * CHECK: %build_fneg = fneg float %F1
- * CHECK: %build_not = xor i32 %P1, -1
- * CHECK: %build_freeze = freeze i32 %P1
- *)
- ignore (build_add p1 p2 "build_add" b);
- ignore (build_nsw_add p1 p2 "build_nsw_add" b);
- ignore (build_nuw_add p1 p2 "build_nuw_add" b);
- ignore (build_fadd f1 f2 "build_fadd" b);
- ignore (build_sub p1 p2 "build_sub" b);
- ignore (build_nsw_sub p1 p2 "build_nsw_sub" b);
- ignore (build_nuw_sub p1 p2 "build_nuw_sub" b);
- ignore (build_fsub f1 f2 "build_fsub" b);
- ignore (build_mul p1 p2 "build_mul" b);
- ignore (build_nsw_mul p1 p2 "build_nsw_mul" b);
- ignore (build_nuw_mul p1 p2 "build_nuw_mul" b);
- ignore (build_fmul f1 f2 "build_fmul" b);
- ignore (build_udiv p1 p2 "build_udiv" b);
- ignore (build_sdiv p1 p2 "build_sdiv" b);
- ignore (build_exact_sdiv p1 p2 "build_exact_sdiv" b);
- ignore (build_fdiv f1 f2 "build_fdiv" b);
- ignore (build_urem p1 p2 "build_urem" b);
- ignore (build_srem p1 p2 "build_srem" b);
- ignore (build_frem f1 f2 "build_frem" b);
- ignore (build_shl p1 p2 "build_shl" b);
- ignore (build_lshr p1 p2 "build_lshl" b);
- ignore (build_ashr p1 p2 "build_ashl" b);
- ignore (build_and p1 p2 "build_and" b);
- ignore (build_or p1 p2 "build_or" b);
- ignore (build_xor p1 p2 "build_xor" b);
- ignore (build_neg p1 "build_neg" b);
- ignore (build_nsw_neg p1 "build_nsw_neg" b);
- ignore (build_nuw_neg p1 "build_nuw_neg" b);
- ignore (build_fneg f1 "build_fneg" b);
- ignore (build_not p1 "build_not" b);
- ignore (build_freeze p1 "build_freeze" b);
- ignore (build_unreachable b)
- end;
-
- group "memory"; begin
- let bb08 = append_block context "Bb08" fn in
- let b = builder_at_end context bb08 in
-
- (* CHECK: %build_alloca = alloca i32
- * CHECK: %build_array_alloca = alloca i32, i32 %P2
- * CHECK: %build_load = load volatile i32, ptr %build_array_alloca, align 4
- * CHECK: store volatile i32 %P2, ptr %build_alloca, align 4
- * CHECK: %build_gep = getelementptr i32, ptr %build_array_alloca, i32 %P2
- * CHECK: %build_in_bounds_gep = getelementptr inbounds i32, ptr %build_array_alloca, i32 %P2
- * CHECK: %build_struct_gep = getelementptr inbounds{{.*}}%build_alloca2, i32 0, i32 1
- * CHECK: %build_atomicrmw = atomicrmw xchg ptr %p, i8 42 seq_cst
- *)
- let alloca = build_alloca i32_type "build_alloca" b in
- let array_alloca = build_array_alloca i32_type p2 "build_array_alloca" b in
-
- let load = build_load i32_type array_alloca "build_load" b in
- ignore(set_alignment 4 load);
- ignore(set_volatile true load);
- insist(true = is_volatile load);
- insist(4 = alignment load);
-
- let store = build_store p2 alloca b in
- ignore(set_volatile true store);
- ignore(set_alignment 4 store);
- insist(true = is_volatile store);
- insist(4 = alignment store);
- ignore(build_gep i32_type array_alloca [| p2 |] "build_gep" b);
- ignore(build_in_bounds_gep i32_type array_alloca [| p2 |]
- "build_in_bounds_gep" b);
-
- let sty = struct_type context [| i32_type; i8_type |] in
- let alloca2 = build_alloca sty "build_alloca2" b in
- ignore(build_struct_gep sty alloca2 1 "build_struct_gep" b);
-
- let p = build_alloca i8_type "p" b in
- ignore(build_atomicrmw AtomicRMWBinOp.Xchg p (const_int i8_type 42)
- AtomicOrdering.SequentiallyConsistent false "build_atomicrmw"
- b);
-
- ignore(build_unreachable b)
- end;
-
- group "string"; begin
- let bb09 = append_block context "Bb09" fn in
- let b = builder_at_end context bb09 in
- let p = build_alloca (pointer_type context) "p" b in
- (* build_global_string is emitted above.
- * CHECK: store{{.*}}build_global_string1{{.*}}p
- * *)
- ignore (build_global_string "stringval" "build_global_string" b);
- let g = build_global_stringptr "stringval" "build_global_string1" b in
- ignore (build_store g p b);
- ignore(build_unreachable b);
- end;
-
- group "phi"; begin
- (* CHECK: PhiNode{{.*}}P1{{.*}}PhiBlock1{{.*}}P2{{.*}}PhiBlock2
- *)
- let b1 = append_block context "PhiBlock1" fn in
- let b2 = append_block context "PhiBlock2" fn in
-
- let jb = append_block context "PhiJoinBlock" fn in
- ignore (build_br jb (builder_at_end context b1));
- ignore (build_br jb (builder_at_end context b2));
- let at_jb = builder_at_end context jb in
-
- let phi = build_phi [(p1, b1)] "PhiNode" at_jb in
- insist ([(p1, b1)] = incoming phi);
-
- add_incoming (p2, b2) phi;
- insist ([(p1, b1); (p2, b2)] = incoming phi);
-
- (* CHECK: %PhiEmptyNode = phi i8
- *)
- let phi_empty = build_empty_phi i8_type "PhiEmptyNode" at_jb in
- insist ([] = incoming phi_empty);
-
- (* can't emit an empty phi to bitcode *)
- add_incoming (const_int i8_type 1, b1) phi_empty;
- add_incoming (const_int i8_type 2, b2) phi_empty;
-
- ignore (build_unreachable at_jb);
- end
-
-(* End-of-file checks for things like metdata and attributes.
- * CHECK: !llvm.module.flags = !{!1}
- * CHECK: !0 = !{i32 0, !"global test metadata"}
- * CHECK: !1 = !{i32 1, !"Debug Info Version", i32 3}
- * CHECK: !2 = !{i32 1, !"metadata test"}
- *)
-
-
-(*===-- Memory Buffer -----------------------------------------------------===*)
-
-let test_memory_buffer () =
- group "memory buffer";
- let buf = MemoryBuffer.of_string "foobar" in
- insist ((MemoryBuffer.as_string buf) = "foobar")
-
-
-(*===-- Writer ------------------------------------------------------------===*)
-
-let test_writer () =
- group "valid";
- insist (match Llvm_analysis.verify_module m with
- | None -> true
- | Some msg -> prerr_string msg; false);
-
- group "writer";
- insist (write_bitcode_file m filename);
-
- dispose_module m
-
-
-(*===-- Driver ------------------------------------------------------------===*)
-
-let _ =
- suite "modules" test_modules;
- suite "contained types" test_contained_types;
- suite "pointer types" test_pointer_types;
- suite "other types" test_other_types;
- suite "conversion" test_conversion;
- suite "target" test_target;
- suite "constants" test_constants;
- suite "attributes" test_attributes;
- suite "global values" test_global_values;
- suite "global variables" test_global_variables;
- suite "uses" test_uses;
- suite "users" test_users;
- suite "aliases" test_aliases;
- suite "functions" test_functions;
- suite "params" test_params;
- suite "basic blocks" test_basic_blocks;
- suite "instructions" test_instructions;
- suite "builder" test_builder;
- suite "memory buffer" test_memory_buffer;
- suite "writer" test_writer; (* Keep this last; it disposes m. *)
- exit !exit_status
diff --git a/llvm-c/OCaml/debuginfo.ml b/llvm-c/OCaml/debuginfo.ml
deleted file mode 100644
index 6ebc7c3..0000000
--- a/llvm-c/OCaml/debuginfo.ml
+++ /dev/null
@@ -1,471 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/debuginfo.ml && cp %S/Utils/Testsuite.ml %t/Testsuite.ml
- * RUN: %ocamlc -g -w +A -package llvm.all_backends -package llvm.target -package llvm.analysis -package llvm.debuginfo -I %t/ -linkpkg %t/Testsuite.ml %t/debuginfo.ml -o %t/executable
- * RUN: %t/executable | FileCheck %s
- * RUN: %ocamlopt -g -w +A -package llvm.all_backends -package llvm.target -package llvm.analysis -package llvm.debuginfo -I %t/ -linkpkg %t/Testsuite.ml %t/debuginfo.ml -o %t/executable
- * RUN: %t/executable | FileCheck %s
- * XFAIL: vg_leak
- *)
-
-open Testsuite
-
-let context = Llvm.global_context ()
-
-let filename = "di_test_file"
-
-let directory = "di_test_dir"
-
-let module_name = "di_test_module"
-
-let null_metadata = Llvm_debuginfo.llmetadata_null ()
-
-let string_of_metadata md =
- Llvm.string_of_llvalue (Llvm.metadata_as_value context md)
-
-let stdout_metadata md = Printf.printf "%s\n" (string_of_metadata md)
-
-let prepare_target llmod =
- Llvm_all_backends.initialize ();
- let triple = Llvm_target.Target.default_triple () in
- let lltarget = Llvm_target.Target.by_triple triple in
- let llmachine = Llvm_target.TargetMachine.create ~triple lltarget in
- let lldly =
- Llvm_target.DataLayout.as_string
- (Llvm_target.TargetMachine.data_layout llmachine)
- in
- let _ = Llvm.set_target_triple triple llmod in
- let _ = Llvm.set_data_layout lldly llmod in
- ()
-
-let new_module () =
- let m = Llvm.create_module context module_name in
- let () = prepare_target m in
- let () = Llvm_debuginfo.set_is_new_dbg_info_format m true in
- insist (Llvm_debuginfo.is_new_dbg_info_format m);
- m
-
-let test_get_module () =
- group "module_level_tests";
- let m = new_module () in
- let cur_ver = Llvm_debuginfo.debug_metadata_version () in
- insist (cur_ver > 0);
- let m_ver = Llvm_debuginfo.get_module_debug_metadata_version m in
- (* We haven't added any debug info to the module *)
- insist (m_ver = 0);
- let dibuilder = Llvm_debuginfo.dibuilder m in
- let di_version_key = "Debug Info Version" in
- let ver =
- Llvm.value_as_metadata @@ Llvm.const_int (Llvm.i32_type context) cur_ver
- in
- let () =
- Llvm.add_module_flag m Llvm.ModuleFlagBehavior.Warning di_version_key ver
- in
- let file_di =
- Llvm_debuginfo.dibuild_create_file dibuilder ~filename ~directory
- in
- stdout_metadata file_di;
- (* CHECK: [[FILE_PTR:<0x[0-9a-f]*>]] = !DIFile(filename: "di_test_file", directory: "di_test_dir")
- *)
- insist
- ( Llvm_debuginfo.di_file_get_filename ~file:file_di = filename
- && Llvm_debuginfo.di_file_get_directory ~file:file_di = directory );
- insist
- ( Llvm_debuginfo.get_metadata_kind file_di
- = Llvm_debuginfo.MetadataKind.DIFileMetadataKind );
- let cu_di =
- Llvm_debuginfo.dibuild_create_compile_unit dibuilder
- Llvm_debuginfo.DWARFSourceLanguageKind.C89 ~file_ref:file_di
- ~producer:"TestGen" ~is_optimized:false ~flags:"" ~runtime_ver:0
- ~split_name:"" Llvm_debuginfo.DWARFEmissionKind.LineTablesOnly ~dwoid:0
- ~di_inlining:false ~di_profiling:false ~sys_root:"" ~sdk:""
- in
- stdout_metadata cu_di;
- (* CHECK: [[CMPUNIT_PTR:<0x[0-9a-f]*>]] = distinct !DICompileUnit(language: DW_LANG_C89, file: [[FILE_PTR]], producer: "TestGen", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false)
- *)
- insist
- ( Llvm_debuginfo.get_metadata_kind cu_di
- = Llvm_debuginfo.MetadataKind.DICompileUnitMetadataKind );
- let m_di =
- Llvm_debuginfo.dibuild_create_module dibuilder ~parent_ref:cu_di
- ~name:module_name ~config_macros:"" ~include_path:"" ~sys_root:""
- in
- insist
- ( Llvm_debuginfo.get_metadata_kind m_di
- = Llvm_debuginfo.MetadataKind.DIModuleMetadataKind );
- insist (Llvm_debuginfo.get_module_debug_metadata_version m = cur_ver);
- stdout_metadata m_di;
- (* CHECK: [[MODULE_PTR:<0x[0-9a-f]*>]] = !DIModule(scope: null, name: "di_test_module")
- *)
- (m, dibuilder, file_di, m_di)
-
-let flags_zero = Llvm_debuginfo.diflags_get Llvm_debuginfo.DIFlag.Zero
-
-let int_ty_di bits dibuilder =
- Llvm_debuginfo.dibuild_create_basic_type dibuilder ~name:"int"
- ~size_in_bits:bits ~encoding:0x05
- (* llvm::dwarf::DW_ATE_signed *) flags_zero
-
-let test_get_function m dibuilder file_di m_di =
- group "function_level_tests";
-
- (* Create a function of type "void foo (int)". *)
- let int_ty_di = int_ty_di 32 dibuilder in
- stdout_metadata int_ty_di;
- (* CHECK: [[INT32_PTR:<0x[0-9a-f]*>]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
- *)
- let int_ptr_ty_di =
- Llvm_debuginfo.dibuild_create_pointer_type dibuilder
- ~pointee_ty:int_ty_di
- ~size_in_bits:32
- ~align_in_bits:32
- ~address_space:0
- ~name:"ptrint"
- in
- stdout_metadata int_ptr_ty_di;
- (* CHECK: [[PTRINT32_PTR:<0x[0-9a-f]*>]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptrint", baseType: [[INT32_PTR]], size: 32, align: 32, dwarfAddressSpace: 0)
- *)
- let param_types = [| null_metadata; int_ty_di; int_ptr_ty_di |] in
- let fty_di =
- Llvm_debuginfo.dibuild_create_subroutine_type dibuilder ~file:file_di
- ~param_types flags_zero
- in
- insist
- ( Llvm_debuginfo.get_metadata_kind fty_di
- = Llvm_debuginfo.MetadataKind.DISubroutineTypeMetadataKind );
- (* To be able to print and verify the type array of the subroutine type,
- * since we have no way to access it from fty_di, we build it again. *)
- let fty_di_args =
- Llvm_debuginfo.dibuild_get_or_create_type_array dibuilder ~data:param_types
- in
- stdout_metadata fty_di_args;
- (* CHECK: [[FARGS_PTR:<0x[0-9a-f]*>]] = !{null, [[INT32_PTR]], [[PTRINT32_PTR]]}
- *)
- stdout_metadata fty_di;
- (* CHECK: [[SBRTNTY_PTR:<0x[0-9a-f]*>]] = !DISubroutineType(types: [[FARGS_PTR]])
- *)
- (* Let's create the LLVM-IR function now. *)
- let name = "tfun" in
- let fty =
- Llvm.function_type (Llvm.void_type context)
- [| Llvm.i32_type context; Llvm.pointer_type context |]
- in
- let f = Llvm.define_function name fty m in
- let f_di =
- Llvm_debuginfo.dibuild_create_function dibuilder ~scope:m_di ~name
- ~linkage_name:name ~file:file_di ~line_no:10 ~ty:fty_di
- ~is_local_to_unit:false ~is_definition:true ~scope_line:10
- ~flags:flags_zero ~is_optimized:false
- in
- stdout_metadata f_di;
- (* CHECK: [[SBPRG_PTR:<0x[0-9a-f]*>]] = distinct !DISubprogram(name: "tfun", linkageName: "tfun", scope: [[MODULE_PTR]], file: [[FILE_PTR]], line: 10, type: [[SBRTNTY_PTR]], scopeLine: 10, spFlags: DISPFlagDefinition, unit: [[CMPUNIT_PTR]])
- *)
- Llvm_debuginfo.set_subprogram f f_di;
- ( match Llvm_debuginfo.get_subprogram f with
- | Some f_di' -> insist (f_di = f_di')
- | None -> insist false );
- insist
- ( Llvm_debuginfo.get_metadata_kind f_di
- = Llvm_debuginfo.MetadataKind.DISubprogramMetadataKind );
- insist (Llvm_debuginfo.di_subprogram_get_line f_di = 10);
- (fty, f, f_di)
-
-let test_bbinstr fty f f_di file_di dibuilder =
- group "basic_block and instructions tests";
- (* Create this pattern:
- * if (arg0 != 0) {
- * foo(arg0, arg1);
- * }
- * return;
- *)
- let arg0 = (Llvm.params f).(0) in
- let arg1 = (Llvm.params f).(1) in
- let builder = Llvm.builder_at_end context (Llvm.entry_block f) in
- let zero = Llvm.const_int (Llvm.i32_type context) 0 in
- let cmpi = Llvm.build_icmp Llvm.Icmp.Ne zero arg0 "cmpi" builder in
- let truebb = Llvm.append_block context "truebb" f in
- let falsebb = Llvm.append_block context "falsebb" f in
- let _ = Llvm.build_cond_br cmpi truebb falsebb builder in
- let foodecl = Llvm.declare_function "foo" fty (Llvm.global_parent f) in
- let _ =
- Llvm.position_at_end truebb builder;
- let scope =
- Llvm_debuginfo.dibuild_create_lexical_block dibuilder ~scope:f_di
- ~file:file_di ~line:9 ~column:4
- in
- let file_of_f_di = Llvm_debuginfo.di_scope_get_file ~scope:f_di in
- let file_of_scope = Llvm_debuginfo.di_scope_get_file ~scope in
- insist
- ( match (file_of_f_di, file_of_scope) with
- | Some file_of_f_di', Some file_of_scope' ->
- file_of_f_di' = file_di && file_of_scope' = file_di
- | _ -> false );
- let foocall = Llvm.build_call fty foodecl [| arg0; arg1 |] "" builder in
- let foocall_loc =
- Llvm_debuginfo.dibuild_create_debug_location context ~line:10 ~column:12
- ~scope
- in
- Llvm_debuginfo.instr_set_debug_loc foocall (Some foocall_loc);
- insist
- ( match Llvm_debuginfo.instr_get_debug_loc foocall with
- | Some foocall_loc' -> foocall_loc' = foocall_loc
- | None -> false );
- stdout_metadata scope;
- (* CHECK: [[BLOCK_PTR:<0x[0-9a-f]*>]] = distinct !DILexicalBlock(scope: [[SBPRG_PTR]], file: [[FILE_PTR]], line: 9, column: 4)
- *)
- stdout_metadata foocall_loc;
- (* CHECK: !DILocation(line: 10, column: 12, scope: [[BLOCK_PTR]])
- *)
- insist
- ( Llvm_debuginfo.di_location_get_scope ~location:foocall_loc = scope
- && Llvm_debuginfo.di_location_get_line ~location:foocall_loc = 10
- && Llvm_debuginfo.di_location_get_column ~location:foocall_loc = 12 );
- insist
- ( Llvm_debuginfo.get_metadata_kind foocall_loc
- = Llvm_debuginfo.MetadataKind.DILocationMetadataKind
- && Llvm_debuginfo.get_metadata_kind scope
- = Llvm_debuginfo.MetadataKind.DILexicalBlockMetadataKind );
- Llvm.build_br falsebb builder
- in
- let _ =
- Llvm.position_at_end falsebb builder;
- Llvm.build_ret_void builder
- in
- (* Printf.printf "%s\n" (Llvm.string_of_llmodule (Llvm.global_parent f)); *)
- ()
-
-let test_global_variable_expression dibuilder f_di m_di =
- group "global variable expression tests";
- let cexpr_di =
- Llvm_debuginfo.dibuild_create_constant_value_expression dibuilder 0
- in
- stdout_metadata cexpr_di;
- (* CHECK: [[DICEXPR:!DIExpression\(DW_OP_constu, 0, DW_OP_stack_value\)]]
- *)
- insist
- ( Llvm_debuginfo.get_metadata_kind cexpr_di
- = Llvm_debuginfo.MetadataKind.DIExpressionMetadataKind );
- let ty = int_ty_di 64 dibuilder in
- stdout_metadata ty;
- (* CHECK: [[INT64TY_PTR:<0x[0-9a-f]*>]] = !DIBasicType(name: "int", size: 64, encoding: DW_ATE_signed)
- *)
- let gvexpr_di =
- Llvm_debuginfo.dibuild_create_global_variable_expression dibuilder
- ~scope:m_di ~name:"my_global" ~linkage:"" ~file:f_di ~line:5 ~ty
- ~is_local_to_unit:true ~expr:cexpr_di ~decl:null_metadata ~align_in_bits:0
- in
- insist
- ( Llvm_debuginfo.get_metadata_kind gvexpr_di
- = Llvm_debuginfo.MetadataKind.DIGlobalVariableExpressionMetadataKind );
- ( match
- Llvm_debuginfo.di_global_variable_expression_get_variable gvexpr_di
- with
- | Some gvexpr_var_di ->
- insist
- ( Llvm_debuginfo.get_metadata_kind gvexpr_var_di
- = Llvm_debuginfo.MetadataKind.DIGlobalVariableMetadataKind );
- stdout_metadata gvexpr_var_di
- (* CHECK: [[GV_PTR:<0x[0-9a-f]*>]] = distinct !DIGlobalVariable(name: "my_global", scope: [[MODULE_PTR]], file: [[FILE_PTR]], line: 5, type: [[INT64TY_PTR]], isLocal: true, isDefinition: true)
- *)
- | None -> insist false );
- stdout_metadata gvexpr_di;
- (* CHECK: [[GVEXP_PTR:<0x[0-9a-f]*>]] = !DIGlobalVariableExpression(var: [[GV_PTR]], expr: [[DICEXPR]])
- *)
- ()
-
-let test_variables f dibuilder file_di fun_di =
- let entry_term = Option.get @@ (Llvm.block_terminator (Llvm.entry_block f)) in
- group "Local and parameter variable tests";
- let ty = int_ty_di 64 dibuilder in
- stdout_metadata ty;
- (* CHECK: [[INT64TY_PTR:<0x[0-9a-f]*>]] = !DIBasicType(name: "int", size: 64, encoding: DW_ATE_signed)
- *)
- let auto_var =
- Llvm_debuginfo.dibuild_create_auto_variable dibuilder ~scope:fun_di
- ~name:"my_local" ~file:file_di ~line:10 ~ty
- ~always_preserve:false flags_zero ~align_in_bits:0
- in
- stdout_metadata auto_var;
- (* CHECK: [[LOCAL_VAR_PTR:<0x[0-9a-f]*>]] = !DILocalVariable(name: "my_local", scope: <{{0x[0-9a-f]*}}>, file: <{{0x[0-9a-f]*}}>, line: 10, type: [[INT64TY_PTR]])
- *)
- let builder = Llvm.builder_before context entry_term in
- let all = Llvm.build_alloca (Llvm.i64_type context) "my_alloca" builder in
- let scope =
- Llvm_debuginfo.dibuild_create_lexical_block dibuilder ~scope:fun_di
- ~file:file_di ~line:9 ~column:4
- in
- let location =
- Llvm_debuginfo.dibuild_create_debug_location
- context ~line:10 ~column:12 ~scope
- in
- let vdi = Llvm_debuginfo.dibuild_insert_declare_before dibuilder ~storage:all
- ~var_info:auto_var ~expr:(Llvm_debuginfo.dibuild_expression dibuilder [||])
- ~location ~instr:entry_term
- in
- let () = Printf.printf "%s\n" (Llvm.string_of_lldbgrecord vdi) in
- (* CHECK: dbg_declare(ptr %my_alloca, ![[#]], !DIExpression(), ![[#]])
- *)
- let arg1 = (Llvm.params f).(1) in
- let arg_var = Llvm_debuginfo.dibuild_create_parameter_variable dibuilder ~scope:fun_di
- ~name:"my_arg" ~argno:1 ~file:file_di ~line:10 ~ty
- ~always_preserve:false flags_zero
- in
- let argdi = Llvm_debuginfo.dibuild_insert_declare_before dibuilder ~storage:arg1
- ~var_info:arg_var ~expr:(Llvm_debuginfo.dibuild_expression dibuilder [||])
- ~location ~instr:entry_term
- in
- let () = Printf.printf "%s\n" (Llvm.string_of_lldbgrecord argdi) in
- (* CHECK: dbg_declare(ptr %1, ![[#]], !DIExpression(), ![[#]])
- *)
- ()
-
-let test_types dibuilder file_di m_di =
- group "type tests";
- let namespace_di =
- Llvm_debuginfo.dibuild_create_namespace dibuilder ~parent_ref:m_di
- ~name:"NameSpace1" ~export_symbols:false
- in
- stdout_metadata namespace_di;
- (* CHECK: [[NAMESPACE_PTR:<0x[0-9a-f]*>]] = !DINamespace(name: "NameSpace1", scope: [[MODULE_PTR]])
- *)
- let int64_ty_di = int_ty_di 64 dibuilder in
- let structty_args = [| int64_ty_di; int64_ty_di; int64_ty_di |] in
- let struct_ty_di =
- Llvm_debuginfo.dibuild_create_struct_type dibuilder ~scope:namespace_di
- ~name:"StructType1" ~file:file_di ~line_number:20 ~size_in_bits:192
- ~align_in_bits:0 flags_zero ~derived_from:null_metadata
- ~elements:structty_args Llvm_debuginfo.DWARFSourceLanguageKind.C89
- ~vtable_holder:null_metadata ~unique_id:"StructType1"
- in
- (* Since there's no way to fetch the element types which is now
- * a type array, we build that again for checking. *)
- let structty_di_eltypes =
- Llvm_debuginfo.dibuild_get_or_create_type_array dibuilder
- ~data:structty_args
- in
- stdout_metadata structty_di_eltypes;
- (* CHECK: [[STRUCTELT_PTR:<0x[0-9a-f]*>]] = !{[[INT64TY_PTR]], [[INT64TY_PTR]], [[INT64TY_PTR]]}
- *)
- stdout_metadata struct_ty_di;
- (* CHECK: [[STRUCT_PTR:<0x[0-9a-f]*>]] = !DICompositeType(tag: DW_TAG_structure_type, name: "StructType1", scope: [[NAMESPACE_PTR]], file: [[FILE_PTR]], line: 20, size: 192, elements: [[STRUCTELT_PTR]], identifier: "StructType1")
- *)
- insist
- ( Llvm_debuginfo.get_metadata_kind struct_ty_di
- = Llvm_debuginfo.MetadataKind.DICompositeTypeMetadataKind );
- let structptr_di =
- Llvm_debuginfo.dibuild_create_pointer_type dibuilder
- ~pointee_ty:struct_ty_di ~size_in_bits:192 ~align_in_bits:0
- ~address_space:0 ~name:""
- in
- stdout_metadata structptr_di;
- (* CHECK: [[STRUCTPTR_PTR:<0x[0-9a-f]*>]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[STRUCT_PTR]], size: 192, dwarfAddressSpace: 0)
- *)
- insist
- ( Llvm_debuginfo.get_metadata_kind structptr_di
- = Llvm_debuginfo.MetadataKind.DIDerivedTypeMetadataKind );
- let enumerator1 =
- Llvm_debuginfo.dibuild_create_enumerator dibuilder ~name:"Test_A" ~value:0
- ~is_unsigned:true
- in
- stdout_metadata enumerator1;
- (* CHECK: [[ENUMERATOR1_PTR:<0x[0-9a-f]*>]] = !DIEnumerator(name: "Test_A", value: 0, isUnsigned: true)
- *)
- let enumerator2 =
- Llvm_debuginfo.dibuild_create_enumerator dibuilder ~name:"Test_B" ~value:1
- ~is_unsigned:true
- in
- stdout_metadata enumerator2;
- (* CHECK: [[ENUMERATOR2_PTR:<0x[0-9a-f]*>]] = !DIEnumerator(name: "Test_B", value: 1, isUnsigned: true)
- *)
- let enumerator3 =
- Llvm_debuginfo.dibuild_create_enumerator dibuilder ~name:"Test_C" ~value:2
- ~is_unsigned:true
- in
- insist
- ( Llvm_debuginfo.get_metadata_kind enumerator1
- = Llvm_debuginfo.MetadataKind.DIEnumeratorMetadataKind
- && Llvm_debuginfo.get_metadata_kind enumerator2
- = Llvm_debuginfo.MetadataKind.DIEnumeratorMetadataKind
- && Llvm_debuginfo.get_metadata_kind enumerator3
- = Llvm_debuginfo.MetadataKind.DIEnumeratorMetadataKind );
- stdout_metadata enumerator3;
- (* CHECK: [[ENUMERATOR3_PTR:<0x[0-9a-f]*>]] = !DIEnumerator(name: "Test_C", value: 2, isUnsigned: true)
- *)
- let elements = [| enumerator1; enumerator2; enumerator3 |] in
- let enumeration_ty_di =
- Llvm_debuginfo.dibuild_create_enumeration_type dibuilder ~scope:namespace_di
- ~name:"EnumTest" ~file:file_di ~line_number:1 ~size_in_bits:64
- ~align_in_bits:0 ~elements ~class_ty:int64_ty_di
- in
- let elements_arr =
- Llvm_debuginfo.dibuild_get_or_create_array dibuilder ~data:elements
- in
- stdout_metadata elements_arr;
- (* CHECK: [[ELEMENTS_PTR:<0x[0-9a-f]*>]] = !{[[ENUMERATOR1_PTR]], [[ENUMERATOR2_PTR]], [[ENUMERATOR3_PTR]]}
- *)
- stdout_metadata enumeration_ty_di;
- (* CHECK: [[ENUMERATION_PTR:<0x[0-9a-f]*>]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "EnumTest", scope: [[NAMESPACE_PTR]], file: [[FILE_PTR]], line: 1, baseType: [[INT64TY_PTR]], size: 64, elements: [[ELEMENTS_PTR]])
- *)
- insist
- ( Llvm_debuginfo.get_metadata_kind enumeration_ty_di
- = Llvm_debuginfo.MetadataKind.DICompositeTypeMetadataKind );
- let int32_ty_di = int_ty_di 32 dibuilder in
- let class_mem1 =
- Llvm_debuginfo.dibuild_create_member_type dibuilder ~scope:namespace_di
- ~name:"Field1" ~file:file_di ~line_number:3 ~size_in_bits:32
- ~align_in_bits:0 ~offset_in_bits:0 flags_zero ~ty:int32_ty_di
- in
- stdout_metadata class_mem1;
- (* CHECK: [[MEMB1_PTR:<0x[0-9a-f]*>]] = !DIDerivedType(tag: DW_TAG_member, name: "Field1", scope: [[NAMESPACE_PTR]], file: [[FILE_PTR]], line: 3, baseType: [[INT32_PTR]], size: 32)
- *)
- insist (Llvm_debuginfo.di_type_get_name class_mem1 = "Field1");
- insist (Llvm_debuginfo.di_type_get_line class_mem1 = 3);
- let class_mem2 =
- Llvm_debuginfo.dibuild_create_member_type dibuilder ~scope:namespace_di
- ~name:"Field2" ~file:file_di ~line_number:4 ~size_in_bits:64
- ~align_in_bits:8 ~offset_in_bits:32 flags_zero ~ty:int64_ty_di
- in
- stdout_metadata class_mem2;
- (* CHECK: [[MEMB2_PTR:<0x[0-9a-f]*>]] = !DIDerivedType(tag: DW_TAG_member, name: "Field2", scope: [[NAMESPACE_PTR]], file: [[FILE_PTR]], line: 4, baseType: [[INT64TY_PTR]], size: 64, align: 8, offset: 32)
- *)
- insist (Llvm_debuginfo.di_type_get_offset_in_bits class_mem2 = 32);
- insist (Llvm_debuginfo.di_type_get_size_in_bits class_mem2 = 64);
- insist (Llvm_debuginfo.di_type_get_align_in_bits class_mem2 = 8);
- let class_elements = [| class_mem1; class_mem2 |] in
- insist
- ( Llvm_debuginfo.get_metadata_kind class_mem1
- = Llvm_debuginfo.MetadataKind.DIDerivedTypeMetadataKind
- && Llvm_debuginfo.get_metadata_kind class_mem2
- = Llvm_debuginfo.MetadataKind.DIDerivedTypeMetadataKind );
- stdout_metadata
- (Llvm_debuginfo.dibuild_get_or_create_type_array dibuilder
- ~data:class_elements);
- (* CHECK: [[CLASSMEM_PTRS:<0x[0-9a-f]*>]] = !{[[MEMB1_PTR]], [[MEMB2_PTR]]}
- *)
- let classty_di =
- Llvm_debuginfo.dibuild_create_class_type dibuilder ~scope:namespace_di
- ~name:"MyClass" ~file:file_di ~line_number:1 ~size_in_bits:96
- ~align_in_bits:0 ~offset_in_bits:0 flags_zero ~derived_from:null_metadata
- ~elements:class_elements ~vtable_holder:null_metadata
- ~template_params_node:null_metadata ~unique_identifier:"MyClass"
- in
- stdout_metadata classty_di;
- (* [[CLASS_PTR:<0x[0-9a-f]*>]] = !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass", scope: [[NAMESPACE_PTR]], file: [[FILE_PTR]], line: 1, size: 96, elements: [[CLASSMEM_PTRS]], identifier: "MyClass")
- *)
- insist
- ( Llvm_debuginfo.get_metadata_kind classty_di
- = Llvm_debuginfo.MetadataKind.DICompositeTypeMetadataKind );
- ()
-
-let () =
- let m, dibuilder, file_di, m_di = test_get_module () in
- let fty, f, fun_di = test_get_function m dibuilder file_di m_di in
- let () = test_bbinstr fty f fun_di file_di dibuilder in
- let () = test_global_variable_expression dibuilder file_di m_di in
- let () = test_variables f dibuilder file_di fun_di in
- let () = test_types dibuilder file_di m_di in
- Llvm_debuginfo.dibuild_finalize dibuilder;
- ( match Llvm_analysis.verify_module m with
- | Some err ->
- prerr_endline ("Verification of module failed: " ^ err);
- exit_status := 1
- | None -> () );
- exit !exit_status
diff --git a/llvm-c/OCaml/diagnostic_handler.ml b/llvm-c/OCaml/diagnostic_handler.ml
deleted file mode 100644
index 491b280..0000000
--- a/llvm-c/OCaml/diagnostic_handler.ml
+++ /dev/null
@@ -1,48 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/diagnostic_handler.ml
- * RUN: %ocamlc -g -w +A -package llvm.bitreader -linkpkg %t/diagnostic_handler.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc | FileCheck %s
- * RUN: %ocamlopt -g -w +A -package llvm.bitreader -linkpkg %t/diagnostic_handler.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc | FileCheck %s
- * XFAIL: vg_leak
- *)
-
-let context = Llvm.global_context ()
-
-let diagnostic_handler d =
- Printf.printf
- "Diagnostic handler called: %s\n" (Llvm.Diagnostic.description d);
- match Llvm.Diagnostic.severity d with
- | Error -> Printf.printf "Diagnostic severity is Error\n"
- | Warning -> Printf.printf "Diagnostic severity is Warning\n"
- | Remark -> Printf.printf "Diagnostic severity is Remark\n"
- | Note -> Printf.printf "Diagnostic severity is Note\n"
-
-let test x = if not x then exit 1 else ()
-
-let _ =
- Llvm.set_diagnostic_handler context (Some diagnostic_handler);
-
- (* corrupt the bitcode *)
- let fn = Sys.argv.(1) ^ ".txt" in
- begin let oc = open_out fn in
- output_string oc "not a bitcode file\n";
- close_out oc
- end;
-
- test begin
- try
- let mb = Llvm.MemoryBuffer.of_file fn in
- let m = begin try
- (* CHECK: Diagnostic handler called: Invalid bitcode signature
- * CHECK: Diagnostic severity is Error
- *)
- Llvm_bitreader.get_module context mb
- with x ->
- Llvm.MemoryBuffer.dispose mb;
- raise x
- end in
- Llvm.dispose_module m;
- false
- with Llvm_bitreader.Error _ ->
- true
- end
diff --git a/llvm-c/OCaml/executionengine.ml b/llvm-c/OCaml/executionengine.ml
deleted file mode 100644
index ec4031f..0000000
--- a/llvm-c/OCaml/executionengine.ml
+++ /dev/null
@@ -1,113 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/executionengine.ml
- * RUN: %ocamlc -g -w +A -thread -package ctypes.foreign,llvm.executionengine -linkpkg %t/executionengine.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -thread -package ctypes.foreign,llvm.executionengine -linkpkg %t/executionengine.ml -o %t/executable
- * RUN: %t/executable
- * REQUIRES: native
- * XFAIL: vg_leak
- *)
-
-open Llvm
-open Llvm_executionengine
-open Llvm_target
-
-(* Note that this takes a moment to link, so it's best to keep the number of
- individual tests low. *)
-
-let context = global_context ()
-let i8_type = Llvm.i8_type context
-let i32_type = Llvm.i32_type context
-let i64_type = Llvm.i64_type context
-let double_type = Llvm.double_type context
-
-let () =
- assert (Llvm_executionengine.initialize ())
-
-let bomb msg =
- prerr_endline msg;
- exit 2
-
-let define_getglobal m pg =
- let fty = function_type i32_type [||] in
- let fn = define_function "getglobal" fty m in
- let b = builder_at_end (global_context ()) (entry_block fn) in
- let g = build_call fty pg [||] "" b in
- ignore (build_ret g b);
- fn
-
-let define_plus m =
- let fn = define_function "plus" (function_type i32_type [| i32_type;
- i32_type |]) m in
- let b = builder_at_end (global_context ()) (entry_block fn) in
- let add = build_add (param fn 0) (param fn 1) "sum" b in
- ignore (build_ret add b);
- fn
-
-let test_executionengine () =
- let open Ctypes in
-
- (* create *)
- let m = create_module (global_context ()) "test_module" in
- let ee = create m in
-
- (* add plus *)
- ignore (define_plus m);
-
- (* declare global variable *)
- ignore (define_global "globvar" (const_int i32_type 23) m);
-
- (* add module *)
- let m2 = create_module (global_context ()) "test_module2" in
- add_module m2 ee;
-
- (* add global mapping *)
- (* BROKEN: see PR20656 *)
- (* let g = declare_function "g" (function_type i32_type [||]) m2 in
- let cg = coerce (Foreign.funptr (void @-> returning int32_t)) (ptr void)
- (fun () -> 42l) in
- add_global_mapping g cg ee;
-
- (* check g *)
- let cg' = get_pointer_to_global g (ptr void) ee in
- if 0 <> ptr_compare cg cg' then bomb "int pointers to g differ";
-
- (* add getglobal *)
- let getglobal = define_getglobal m2 g in*)
-
- (* run_static_ctors *)
- run_static_ctors ee;
-
- (* get a handle on globvar *)
- let varh = get_global_value_address "globvar" int32_t ee in
- if 23l <> varh then bomb "get_global_value_address didn't work";
-
- (* call plus *)
- let cplusty = Foreign.funptr (int32_t @-> int32_t @-> returning int32_t) in
- let cplus = get_function_address "plus" cplusty ee in
- if 4l <> cplus 2l 2l then bomb "plus didn't work";
-
- (* call getglobal *)
- (* let cgetglobalty = Foreign.funptr (void @-> returning int32_t) in
- let cgetglobal = get_pointer_to_global getglobal cgetglobalty ee in
- if 42l <> cgetglobal () then bomb "getglobal didn't work"; *)
-
- (* remove_module *)
- remove_module m2 ee;
- dispose_module m2;
-
- (* run_static_dtors *)
- run_static_dtors ee;
-
- (* Show that the data layout binding links and runs.*)
- let dl = data_layout ee in
-
- (* Demonstrate that a garbage pointer wasn't returned. *)
- let ty = DataLayout.intptr_type context dl in
- if ty != i32_type && ty != i64_type then bomb "target_data did not work";
-
- (* dispose *)
- dispose ee
-
-let () =
- test_executionengine ();
- Gc.compact ()
diff --git a/llvm-c/OCaml/ext_exc.ml b/llvm-c/OCaml/ext_exc.ml
deleted file mode 100644
index 8ff19cd..0000000
--- a/llvm-c/OCaml/ext_exc.ml
+++ /dev/null
@@ -1,25 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/ext_exc.ml
- * RUN: %ocamlc -g -w +A -package llvm.bitreader -linkpkg %t/ext_exc.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.bitreader -linkpkg %t/ext_exc.ml -o %t/executable
- * RUN: %t/executable
- * XFAIL: vg_leak
- *)
-
-let context = Llvm.global_context ()
-
-let diagnostic_handler _ = ()
-
-(* This used to crash, we must not use 'external' in .mli files, but 'val' if we
- * want the let _ bindings executed, see http://caml.inria.fr/mantis/view.php?id=4166 *)
-let _ =
- Llvm.set_diagnostic_handler context (Some diagnostic_handler);
- try
- ignore (Llvm_bitreader.get_module context (Llvm.MemoryBuffer.of_stdin ()))
- with
- Llvm_bitreader.Error _ -> ();;
-let _ =
- try
- ignore (Llvm.MemoryBuffer.of_file "/path/to/nonexistent/file")
- with
- Llvm.IoError _ -> ();;
diff --git a/llvm-c/OCaml/irreader.ml b/llvm-c/OCaml/irreader.ml
deleted file mode 100644
index 7d8e4a9..0000000
--- a/llvm-c/OCaml/irreader.ml
+++ /dev/null
@@ -1,59 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/irreader.ml
- * RUN: %ocamlc -g -w +A -package llvm.irreader -linkpkg %t/irreader.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.irreader -linkpkg %t/irreader.ml -o %t/executable
- * RUN: %t/executable
- * XFAIL: vg_leak
- *)
-
-(* Note: It takes several seconds for ocamlopt to link an executable with
- libLLVMCore.a, so it's better to write a big test than a bunch of
- little ones. *)
-
-open Llvm
-open Llvm_irreader
-
-let context = global_context ()
-
-(* Tiny unit test framework - really just to help find which line is busted *)
-let print_checkpoints = false
-
-let suite name f =
- if print_checkpoints then
- prerr_endline (name ^ ":");
- f ()
-
-let _ =
- Printexc.record_backtrace true
-
-let insist cond =
- if not cond then failwith "insist"
-
-
-(*===-- IR Reader ---------------------------------------------------------===*)
-
-let test_irreader () =
- begin
- let buf = MemoryBuffer.of_string "@foo = global i32 42" in
- let m = parse_ir context buf in
- match lookup_global "foo" m with
- | Some foo ->
- insist ((global_initializer foo) = (Some (const_int (i32_type context) 42)))
- | None ->
- failwith "global"
- end;
-
- begin
- let buf = MemoryBuffer.of_string "@foo = global garble" in
- try
- ignore (parse_ir context buf);
- failwith "parsed"
- with Llvm_irreader.Error _ ->
- ()
- end
-
-
-(*===-- Driver ------------------------------------------------------------===*)
-
-let _ =
- suite "irreader" test_irreader
diff --git a/llvm-c/OCaml/linker.ml b/llvm-c/OCaml/linker.ml
deleted file mode 100644
index 6375be8..0000000
--- a/llvm-c/OCaml/linker.ml
+++ /dev/null
@@ -1,65 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/linker.ml
- * RUN: %ocamlc -g -w +A -package llvm.linker -linkpkg %t/linker.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.linker -linkpkg %t/linker.ml -o %t/executable
- * RUN: %t/executable
- * XFAIL: vg_leak
- *)
-
-(* Note: It takes several seconds for ocamlopt to link an executable with
- libLLVMCore.a, so it's better to write a big test than a bunch of
- little ones. *)
-
-open Llvm
-open Llvm_linker
-
-let context = global_context ()
-let void_type = Llvm.void_type context
-
-let diagnostic_handler _ = ()
-
-(* Tiny unit test framework - really just to help find which line is busted *)
-let print_checkpoints = false
-
-let suite name f =
- if print_checkpoints then
- prerr_endline (name ^ ":");
- f ()
-
-
-(*===-- Linker -----------------------------------------------------------===*)
-
-let test_linker () =
- set_diagnostic_handler context (Some diagnostic_handler);
-
- let fty = function_type void_type [| |] in
-
- let make_module name =
- let m = create_module context name in
- let fn = define_function ("fn_" ^ name) fty m in
- ignore (build_ret_void (builder_at_end context (entry_block fn)));
- m
- in
-
- let m1 = make_module "one"
- and m2 = make_module "two" in
- link_modules m1 m2;
- dispose_module m1;
-
- let m1 = make_module "one"
- and m2 = make_module "two" in
- link_modules m1 m2;
- dispose_module m1;
-
- let m1 = make_module "one"
- and m2 = make_module "one" in
- try
- link_modules m1 m2;
- failwith "must raise"
- with Error _ ->
- dispose_module m1
-
-(*===-- Driver ------------------------------------------------------------===*)
-
-let _ =
- suite "linker" test_linker
diff --git a/llvm-c/OCaml/lit.local.cfg b/llvm-c/OCaml/lit.local.cfg
deleted file mode 100644
index 03e71e9..0000000
--- a/llvm-c/OCaml/lit.local.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-config.suffixes = [".ml"]
-
-if not "ocaml" in config.root.llvm_bindings:
- config.unsupported = True
diff --git a/llvm-c/OCaml/passbuilder.ml b/llvm-c/OCaml/passbuilder.ml
deleted file mode 100644
index 180cc3a..0000000
--- a/llvm-c/OCaml/passbuilder.ml
+++ /dev/null
@@ -1,74 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/passbuilder.ml
- * RUN: %ocamlc -g -w +A -package llvm.passbuilder -package llvm.all_backends -linkpkg %t/passbuilder.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.passbuilder -package llvm.all_backends -linkpkg %t/passbuilder.ml -o %t/executable
- * RUN: %t/executable
- * XFAIL: vg_leak
- *)
-
-let () = Llvm_all_backends.initialize ()
-
-(*===-- Fixture -----------------------------------------------------------===*)
-
-let context = Llvm.global_context ()
-
-let m = Llvm.create_module context "mymodule"
-
-let () =
- let ty =
- Llvm.function_type (Llvm.void_type context)
- [| Llvm.i1_type context;
- Llvm.pointer_type context;
- Llvm.pointer_type context |]
- in
- let foo = Llvm.define_function "foo" ty m in
- let entry = Llvm.entry_block foo in
- let builder = Llvm.builder_at_end context entry in
- ignore
- (Llvm.build_store
- (Llvm.const_int (Llvm.i8_type context) 42) (Llvm.param foo 1) builder);
- let loop = Llvm.append_block context "loop" foo in
- Llvm.position_at_end loop builder;
- ignore
- (Llvm.build_load (Llvm.i8_type context) (Llvm.param foo 2) "tmp1" builder);
- ignore (Llvm.build_br loop builder);
- let exit = Llvm.append_block context "exit" foo in
- Llvm.position_at_end exit builder;
- ignore (Llvm.build_ret_void builder);
- Llvm.position_at_end entry builder;
- ignore (Llvm.build_cond_br (Llvm.param foo 0) loop exit builder)
-
-let target =
- Llvm_target.Target.by_triple (Llvm_target.Target.default_triple ())
-
-let machine =
- Llvm_target.TargetMachine.create
- ~triple:(Llvm_target.Target.default_triple ()) target
-
-let options = Llvm_passbuilder.create_passbuilder_options ()
-
-(*===-- PassBuilder -------------------------------------------------------===*)
-let () =
- Llvm_passbuilder.passbuilder_options_set_verify_each options true;
- Llvm_passbuilder.passbuilder_options_set_debug_logging options true;
- Llvm_passbuilder.passbuilder_options_set_loop_interleaving options true;
- Llvm_passbuilder.passbuilder_options_set_loop_vectorization options true;
- Llvm_passbuilder.passbuilder_options_set_slp_vectorization options true;
- Llvm_passbuilder.passbuilder_options_set_loop_unrolling options true;
- Llvm_passbuilder.passbuilder_options_set_forget_all_scev_in_loop_unroll
- options true;
- Llvm_passbuilder.passbuilder_options_set_licm_mssa_opt_cap options 2;
- Llvm_passbuilder.passbuilder_options_set_licm_mssa_no_acc_for_promotion_cap
- options 2;
- Llvm_passbuilder.passbuilder_options_set_call_graph_profile options true;
- Llvm_passbuilder.passbuilder_options_set_merge_functions options true;
- Llvm_passbuilder.passbuilder_options_set_inliner_threshold options 2;
- match Llvm_passbuilder.run_passes m "no-op-module" machine options with
- | Error e ->
- prerr_endline e;
- assert false
- | Ok () -> ()
-
-let () =
- Llvm_passbuilder.dispose_passbuilder_options options;
- Llvm.dispose_module m
diff --git a/llvm-c/OCaml/target.ml b/llvm-c/OCaml/target.ml
deleted file mode 100644
index e9465fd..0000000
--- a/llvm-c/OCaml/target.ml
+++ /dev/null
@@ -1,111 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/target.ml
- * RUN: %ocamlc -g -w +A -package llvm.target -package llvm.all_backends -linkpkg %t/target.ml -o %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.target -package llvm.all_backends -linkpkg %t/target.ml -o %t/executable
- * RUN: %t/executable %t/bitcode.bc
- * XFAIL: vg_leak
- *)
-
-(* Note: It takes several seconds for ocamlopt to link an executable with
- libLLVMCore.a, so it's better to write a big test than a bunch of
- little ones. *)
-
-open Llvm
-open Llvm_target
-
-let () = Llvm_all_backends.initialize ()
-
-let context = global_context ()
-let i32_type = Llvm.i32_type context
-let i64_type = Llvm.i64_type context
-
-(* Tiny unit test framework - really just to help find which line is busted *)
-let print_checkpoints = false
-
-let _ =
- Printexc.record_backtrace true
-
-let assert_equal a b =
- if a <> b then failwith "assert_equal"
-
-
-(*===-- Fixture -----------------------------------------------------------===*)
-
-let filename = Sys.argv.(1)
-let m = create_module context filename
-
-let target = Target.by_triple (Target.default_triple ())
-
-let machine = TargetMachine.create (Target.default_triple ()) target
-
-(*===-- Data Layout -------------------------------------------------------===*)
-
-let test_target_data () =
- let module DL = DataLayout in
- let layout = "e-p:32:32-f64:32:64-v64:32:64-v128:32:128-n32-S32" in
- let dl = DL.of_string layout in
- let sty = struct_type context [| i32_type; i64_type |] in
-
- assert_equal (DL.as_string dl) layout;
- assert_equal (DL.byte_order dl) Endian.Little;
- assert_equal (DL.pointer_size dl) 4;
- assert_equal (DL.intptr_type context dl) i32_type;
- assert_equal (DL.qualified_pointer_size 0 dl) 4;
- assert_equal (DL.qualified_intptr_type context 0 dl) i32_type;
- assert_equal (DL.size_in_bits sty dl) (Int64.of_int 96);
- assert_equal (DL.store_size sty dl) (Int64.of_int 12);
- assert_equal (DL.abi_size sty dl) (Int64.of_int 12);
- assert_equal (DL.stack_align sty dl) 4;
- assert_equal (DL.preferred_align sty dl) 8;
- assert_equal (DL.preferred_align_of_global (declare_global sty "g" m) dl) 8;
- assert_equal (DL.element_at_offset sty (Int64.of_int 1) dl) 0;
- assert_equal (DL.offset_of_element sty 1 dl) (Int64.of_int 4)
-
-
-(*===-- Target ------------------------------------------------------------===*)
-
-let test_target () =
- let module T = Target in
- ignore (T.succ target);
- ignore (T.name target);
- ignore (T.description target);
- ignore (T.has_jit target);
- ignore (T.has_target_machine target);
- ignore (T.has_asm_backend target)
-
-
-(*===-- Target Machine ----------------------------------------------------===*)
-
-let test_target_machine () =
- let module TM = TargetMachine in
- assert_equal (TM.target machine) target;
- assert_equal (TM.triple machine) (Target.default_triple ());
- assert_equal (TM.cpu machine) "";
- assert_equal (TM.features machine) "";
- ignore (TM.data_layout machine);
- TM.set_verbose_asm true machine
-
-
-(*===-- Code Emission -----------------------------------------------------===*)
-
-let test_code_emission () =
- TargetMachine.emit_to_file m CodeGenFileType.ObjectFile filename machine;
- try
- TargetMachine.emit_to_file m CodeGenFileType.ObjectFile
- "/nonexistent/file" machine;
- failwith "must raise"
- with Llvm_target.Error _ ->
- ();
-
- let buf = TargetMachine.emit_to_memory_buffer m CodeGenFileType.ObjectFile
- machine in
- Llvm.MemoryBuffer.dispose buf
-
-
-(*===-- Driver ------------------------------------------------------------===*)
-
-let _ =
- test_target_data ();
- test_target ();
- test_target_machine ();
- test_code_emission ();
- dispose_module m
diff --git a/llvm-c/OCaml/transform_utils.ml b/llvm-c/OCaml/transform_utils.ml
deleted file mode 100644
index f951a0d..0000000
--- a/llvm-c/OCaml/transform_utils.ml
+++ /dev/null
@@ -1,21 +0,0 @@
-(* RUN: rm -rf %t && mkdir -p %t && cp %s %t/transform_utils.ml
- * RUN: %ocamlc -g -w +A -package llvm.transform_utils -linkpkg %t/transform_utils.ml -o %t/executable
- * RUN: %t/executable
- * RUN: %ocamlopt -g -w +A -package llvm.transform_utils -linkpkg %t/transform_utils.ml -o %t/executable
- * RUN: %t/executable
- * XFAIL: vg_leak
- *)
-
-open Llvm
-open Llvm_transform_utils
-
-let context = global_context ()
-
-let test_clone_module () =
- let m = create_module context "mod" in
- let m' = clone_module m in
- if m == m' then failwith "m == m'";
- if string_of_llmodule m <> string_of_llmodule m' then failwith "string_of m <> m'"
-
-let () =
- test_clone_module ()
diff --git a/llvm_c_test/debuginfo.py b/llvm_c_test/debuginfo.py
index a5ce04f..afc33e2 100644
--- a/llvm_c_test/debuginfo.py
+++ b/llvm_c_test/debuginfo.py
@@ -28,7 +28,7 @@ def get_di_tag():
# Create DIBuilder
with mod.create_dibuilder() as dib:
# Create file
- file_md = dib.create_file("metadata.c", ".")
+ file_md = dib.file("metadata.c", ".")
# Create struct type (DW_TAG_structure_type = 0x13)
struct_md = dib.create_struct_type(
@@ -60,7 +60,7 @@ def di_type_get_name():
# Create DIBuilder
with mod.create_dibuilder() as dib:
# Create file
- file_md = dib.create_file("metadata.c", ".")
+ file_md = dib.file("metadata.c", ".")
# Create struct type with name
name = "TestClass"
@@ -157,23 +157,23 @@ def test_dibuilder():
# Create DIBuilder
with mod.create_dibuilder() as dib:
# Create file
- file_md = dib.create_file(filename, ".")
+ file_md = dib.file(filename, ".")
# Create compile unit
- compile_unit = dib.create_compile_unit(
- llvm.DWARFSourceLanguageC,
- file_md,
- "llvm-c-test",
- False,
- "",
- 0,
- "",
- llvm.DWARFEmissionFull,
- 0,
- False,
- False,
- "/",
- "",
+ compile_unit = dib.compile_unit(
+ language=llvm.DwarfLanguage.C,
+ file=file_md,
+ producer="llvm-c-test",
+ is_optimized=False,
+ flags="",
+ runtime_ver=0,
+ split_name="",
+ kind=llvm.DwarfEmissionKind.Full,
+ dwo_id=0,
+ split_debug_inlining=False,
+ debug_info_for_profiling=False,
+ sys_root="/",
+ sdk="",
)
# Create module
@@ -265,7 +265,7 @@ def test_dibuilder():
struct_dbg_ptr_ty = dib.create_pointer_type(struct_dbg_ty, 192, 0, 0, "")
# Add to named metadata
- mod.add_named_metadata_operand("FooType", struct_dbg_ptr_ty)
+ mod.named_metadata["FooType"].append(struct_dbg_ptr_ty)
# Create function
i64_type = ctx.types.i64
@@ -289,7 +289,7 @@ def test_dibuilder():
)
# Create debug location for parameters
- foo_param_location = ctx.create_debug_location(
+ foo_param_location = ctx.debug_location(
42, 0, replaceable_function_md, None
)
@@ -354,7 +354,7 @@ def test_dibuilder():
# Create another basic block for variables
foo_var_block = foo_function.append_basic_block("vars")
- foo_vars_location = ctx.create_debug_location(
+ foo_vars_location = ctx.debug_location(
43, 0, function_md, None
)
@@ -404,7 +404,7 @@ def test_dibuilder():
enum_test = dib.create_enumeration_type(
namespace, "EnumTest", file_md, 0, 64, 0, enumerators_test, int64_ty
)
- mod.add_named_metadata_operand("EnumTest", enum_test)
+ mod.named_metadata["EnumTest"].append(enum_test)
# Create UInt128 type and large enumerators
uint128_ty = dib.create_basic_type("UInt128", 128, 0, llvm.DIFlagZero)
@@ -429,7 +429,7 @@ def test_dibuilder():
large_enumerators,
uint128_ty,
)
- mod.add_named_metadata_operand("LargeEnumTest", large_enum_test)
+ mod.named_metadata["LargeEnumTest"].append(large_enum_test)
# Create subrange type with metadata bounds
foo_val3 = i64_type.constant(8)
@@ -442,7 +442,7 @@ def test_dibuilder():
subrange_md_ty = dib.create_subrange_type(
file_md, "foo", 42, file_md, 64, 0, 0, int64_ty, lo, hi, strd, bias
)
- mod.add_named_metadata_operand("SubrangeType", subrange_md_ty)
+ mod.named_metadata["SubrangeType"].append(subrange_md_ty)
# Create set types
set_md_ty1 = dib.create_set_type(
@@ -451,8 +451,8 @@ def test_dibuilder():
set_md_ty2 = dib.create_set_type(
file_md, "subrangeset", file_md, 42, 64, 0, subrange_md_ty
)
- mod.add_named_metadata_operand("SetType1", set_md_ty1)
- mod.add_named_metadata_operand("SetType2", set_md_ty2)
+ mod.named_metadata["SetType1"].append(set_md_ty1)
+ mod.named_metadata["SetType2"].append(set_md_ty2)
# Create dynamic array type
dyn_subscripts = [dib.get_or_create_subrange(0, 10)]
@@ -473,7 +473,7 @@ def test_dibuilder():
rank_expr,
None,
)
- mod.add_named_metadata_operand("DynType", dynamic_array_md_ty)
+ mod.named_metadata["DynType"].append(dynamic_array_md_ty)
# Create forward declaration
struct_p_ty = dib.create_forward_decl(
@@ -485,7 +485,7 @@ def test_dibuilder():
struct_elts_array = [int64_ty, int64_ty, int32_ty]
class_arr = dib.get_or_create_array(struct_elts_array)
dib.replace_arrays([struct_p_ty], [class_arr])
- mod.add_named_metadata_operand("ClassType", struct_p_ty)
+ mod.named_metadata["ClassType"].append(struct_p_ty)
# Build IR instructions
with foo_entry_block.create_builder() as builder:
diff --git a/llvm_c_test/disassemble.py b/llvm_c_test/disassemble.py
index b0e0cfe..ac5f41e 100644
--- a/llvm_c_test/disassemble.py
+++ b/llvm_c_test/disassemble.py
@@ -104,11 +104,6 @@ def disassemble() -> int:
Returns:
Exit code (0 for success)
"""
- # Initialize all targets
- llvm.initialize_all_target_infos()
- llvm.initialize_all_target_mcs()
- llvm.initialize_all_disassemblers()
-
# Process stdin
tokenize_stdin(handle_line)
diff --git a/llvm_c_test/echo.py b/llvm_c_test/echo.py
index 9e8b988..4bb529d 100644
--- a/llvm_c_test/echo.py
+++ b/llvm_c_test/echo.py
@@ -1029,13 +1029,8 @@ class FunCloner:
if src.can_use_fast_math_flags:
dst.fast_math_flags = src.fast_math_flags
- # Copy instruction metadata
- ctx = self.module.context
- all_metadata = src.instruction_get_all_metadata_other_than_debug_loc()
- for i in range(len(all_metadata)):
- kind = all_metadata.get_kind(i)
- md = all_metadata.get_metadata(i)
- dst.set_metadata(kind, md, ctx)
+ # Copy instruction metadata.
+ src.metadata.copy_to(dst)
check_value_kind(dst, llvm.ValueKind.Instruction)
self.vmap[src] = dst
@@ -1239,30 +1234,11 @@ def declare_symbols(src: llvm.Module, m: llvm.Module) -> None:
cur = next_i
- # Declare named metadata
- cur_md = src.first_named_metadata
- end_md = src.last_named_metadata
- if cur_md is None:
- if end_md is not None:
- raise RuntimeError("Range has an end but no beginning")
- else:
- while True:
- name = cur_md.name
- if m.get_named_metadata(name):
- raise RuntimeError("Named Metadata Node already cloned")
- m.add_named_metadata(name)
-
- next_md = cur_md.next
- if next_md is None:
- if cur_md != end_md:
- raise RuntimeError("")
- break
-
- prev_md = next_md.prev
- if prev_md != cur_md:
- raise RuntimeError("Next.Previous global is not Current")
-
- cur_md = next_md
+ # Declare named metadata.
+ for name in src.named_metadata:
+ if m.named_metadata.get(name) is not None:
+ raise RuntimeError("Named Metadata Node already cloned")
+ _ = m.named_metadata[name]
def clone_symbols(src: llvm.Module, m: llvm.Module) -> None:
@@ -1284,13 +1260,8 @@ def clone_symbols(src: llvm.Module, m: llvm.Module) -> None:
if init:
g.initializer = clone_constant(init, m)
- # Copy global metadata
- ctx = m.context
- all_metadata = cur.global_copy_all_metadata()
- for i in range(len(all_metadata)):
- kind = all_metadata.get_kind(i)
- md = all_metadata.get_metadata(i)
- g.set_metadata(kind, md, ctx)
+ # Copy global metadata.
+ cur.metadata.copy_to(g)
g.is_global_constant = cur.is_global_constant
g.is_thread_local = cur.is_thread_local
@@ -1335,13 +1306,8 @@ def clone_symbols(src: llvm.Module, m: llvm.Module) -> None:
raise RuntimeError("Could not find personality function")
fun.personality_fn = p
- # Copy function metadata
- ctx = m.context
- all_metadata = cur.global_copy_all_metadata()
- for i in range(len(all_metadata)):
- kind = all_metadata.get_kind(i)
- md = all_metadata.get_metadata(i)
- fun.set_metadata(kind, md, ctx)
+ # Copy function metadata.
+ cur.metadata.copy_to(fun)
# Copy prefix and prologue data
if cur.has_prefix_data:
@@ -1433,37 +1399,13 @@ def clone_symbols(src: llvm.Module, m: llvm.Module) -> None:
cur = next_i
- # Clone named metadata contents
- cur_md = src.first_named_metadata
- end_md = src.last_named_metadata
- if cur_md is None:
- if end_md is not None:
- raise RuntimeError("Range has an end but no beginning")
- else:
- while True:
- name = cur_md.name
- named_md = m.get_named_metadata(name)
- if not named_md:
- raise RuntimeError("Named MD Node must have been declared already")
-
- operand_count = src.get_named_metadata_num_operands(name)
- operands = src.get_named_metadata_operands(name)
- for op in operands:
- # Convert value to metadata before adding
- md = op.as_metadata()
- m.add_named_metadata_operand(name, md)
-
- next_md = cur_md.next
- if next_md is None:
- if cur_md != end_md:
- raise RuntimeError("Last Named MD Node does not match End")
- break
-
- prev_md = next_md.prev
- if prev_md != cur_md:
- raise RuntimeError("Next.Previous Named MD Node is not Current")
-
- cur_md = next_md
+ # Clone named metadata contents.
+ for name in src.named_metadata:
+ named_md = m.named_metadata.get(name)
+ if named_md is None:
+ raise RuntimeError("Named MD Node must have been declared already")
+ for md in src.named_metadata[name]:
+ named_md.append(md)
def echo() -> int:
diff --git a/llvm_c_test/metadata.py b/llvm_c_test/metadata.py
index 378e1f3..f8c2d63 100644
--- a/llvm_c_test/metadata.py
+++ b/llvm_c_test/metadata.py
@@ -26,7 +26,7 @@ def add_named_metadata_operand():
# First convert value to metadata, then create node
md = val.as_metadata()
md_node = ctx.md_node([md])
- mod.add_named_metadata_operand("name", md_node)
+ mod.named_metadata["name"].append(md_node)
return 0
except Exception as e:
@@ -62,8 +62,7 @@ def set_metadata():
md = val.as_metadata()
md_node = ctx.md_node([md])
- kind_id = llvm.get_md_kind_id("kind")
- ret_inst.set_metadata(kind_id, md_node, ctx)
+ ret_inst.metadata["kind"] = md_node
# Delete the instruction
ret_inst.delete_instruction()
@@ -79,18 +78,14 @@ def replace_md_operand():
try:
with llvm.create_context() as ctx:
with ctx.create_module("Mod"):
- # Build MDNode("foo"), then replace operand 0 with MDString("bar").
- string1_md = ctx.md_string("foo")
- node_md = ctx.md_node([string1_md])
- value = node_md.as_value(ctx)
+ # Build MDNode("foo") and inspect operand 0.
+ string_md = ctx.md_string("foo")
+ node_md = ctx.md_node([string_md])
- string2_md = ctx.md_string("bar")
- value.replace_md_node_operand_with(0, string2_md)
-
- # Verify replacement took effect.
- operand = value.get_operand(0)
- if operand.as_metadata() != string2_md:
- raise AssertionError("Metadata operand replacement did not apply")
+ # The public Python API keeps Metadata as Metadata; MDNode
+ # children are inspected through Metadata.operands.
+ if node_md.operands[0] != string_md:
+ raise AssertionError("Metadata operand access did not apply")
return 0
except Exception as e:
@@ -107,17 +102,15 @@ def is_a_value_as_metadata():
i32 = ctx.types.i32
val = i32.constant(0, False).as_metadata()
md_node = ctx.md_node([val])
- md_val = md_node.as_value(ctx)
- if not md_val.is_value_as_metadata:
+ if not md_node.operands[0].is_value:
raise AssertionError("Expected ValueAsMetadata for value-backed MD")
# MDNode built from MDString should NOT be ValueAsMetadata.
string_md = ctx.md_string("foo")
string_node = ctx.md_node([string_md])
- string_val = string_node.as_value(ctx)
- if string_val.is_value_as_metadata:
+ if string_node.operands[0].is_value:
raise AssertionError("Expected non-ValueAsMetadata for string-backed MD")
return 0
diff --git a/llvm_c_test/targets.py b/llvm_c_test/targets.py
index 9d0555d..29d6bd2 100644
--- a/llvm_c_test/targets.py
+++ b/llvm_c_test/targets.py
@@ -9,10 +9,6 @@ import llvm
def targets_list():
"""List all registered LLVM targets."""
- # Initialize all target info and targets
- llvm.initialize_all_target_infos()
- llvm.initialize_all_targets()
-
# Iterate through targets
target = llvm.get_first_target()
while target is not None:
diff --git a/src/llvm-nanobind.cpp b/src/llvm-nanobind.cpp
index 2fe2fd8..0a3278a 100644
--- a/src/llvm-nanobind.cpp
+++ b/src/llvm-nanobind.cpp
@@ -22,8 +22,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -127,6 +129,15 @@ struct LLVMAssertionError : std::runtime_error {
using std::runtime_error::runtime_error;
};
+static std::string consume_llvm_error(LLVMErrorRef err) {
+ if (!err)
+ return "";
+ char *msg = LLVMGetErrorMessage(err);
+ std::string result = msg ? msg : "Unknown error";
+ LLVMDisposeErrorMessage(msg);
+ return result;
+}
+
static unsigned lookup_enum_attribute_kind(const std::string &name) {
return LLVMGetEnumAttributeKindForName(name.c_str(), name.size());
}
@@ -138,6 +149,141 @@ static unsigned require_enum_attribute_kind(const std::string &name) {
return kind_id;
}
+static std::string trim_copy(const std::string &text) {
+ size_t start = 0;
+ while (start < text.size() &&
+ std::isspace(static_cast(text[start])))
+ ++start;
+ size_t end = text.size();
+ while (end > start && std::isspace(static_cast(text[end - 1])))
+ --end;
+ return text.substr(start, end - start);
+}
+
+static std::string lower_copy(std::string text) {
+ for (char &c : text)
+ c = static_cast(std::tolower(static_cast(c)));
+ return text;
+}
+
+static std::string normalize_memory_keyword(const std::string &text) {
+ std::string result;
+ for (char c : trim_copy(text)) {
+ unsigned char uc = static_cast(c);
+ if (std::isspace(uc) || c == '_' || c == '-')
+ continue;
+ result.push_back(static_cast(std::tolower(uc)));
+ }
+ return result;
+}
+
+static uint64_t memory_access_bits(const std::string &access) {
+ std::string key = normalize_memory_keyword(access);
+ if (key == "none" || key == "nomodref" || key == "noaccess")
+ return 0;
+ if (key == "read" || key == "ref" || key == "readonly")
+ return 1;
+ if (key == "write" || key == "mod" || key == "writeonly")
+ return 2;
+ if (key == "readwrite" || key == "modref")
+ return 3;
+ throw LLVMAssertionError("Unknown memory access effect: " + access);
+}
+
+static bool memory_effects_has_errno_mem() {
+ unsigned major = 0;
+ LLVMGetVersion(&major, nullptr, nullptr);
+ return major >= 21;
+}
+
+static uint64_t encode_all_memory_locations(uint64_t access) {
+ uint64_t encoded = access | (access << 2);
+ if (memory_effects_has_errno_mem()) {
+ // LLVM 21 added ErrnoMem between InaccessibleMem and Other.
+ encoded |= (access << 4) | (access << 6);
+ } else {
+ encoded |= access << 4;
+ }
+ return encoded;
+}
+
+struct EncodedMemoryLocationEffect {
+ uint64_t encoded = 0;
+ unsigned seen_bit = 0;
+};
+
+static EncodedMemoryLocationEffect
+encode_memory_location_effect(const std::string &location, uint64_t access) {
+ std::string key = normalize_memory_keyword(location);
+ if (key == "argmem" || key == "arg" || key == "argument" ||
+ key == "argumentmem") {
+ return {access, 1u << 0};
+ }
+ if (key == "inaccessiblemem" || key == "inaccessible") {
+ return {access << 2, 1u << 1};
+ }
+ if (key == "errnomem" || key == "errno") {
+ if (!memory_effects_has_errno_mem()) {
+ throw LLVMAssertionError(
+ "memory effect location 'errnomem' requires LLVM 21+");
+ }
+ return {access << 4, 1u << 2};
+ }
+ if (key == "other") {
+ if (memory_effects_has_errno_mem())
+ return {access << 6, 1u << 3};
+ return {access << 4, 1u << 2};
+ }
+ throw LLVMAssertionError("Unknown memory effect location: " + location);
+}
+
+static uint64_t encode_memory_effects(const std::string &effects) {
+ std::string spec = trim_copy(effects);
+ if (spec.empty())
+ throw LLVMAssertionError("memory effects cannot be empty");
+
+ std::string lowered = lower_copy(spec);
+ if (lowered.size() >= 8 && lowered.compare(0, 7, "memory(") == 0 &&
+ spec.back() == ')') {
+ spec = trim_copy(spec.substr(7, spec.size() - 8));
+ }
+
+ if (spec.find(':') == std::string::npos) {
+ return encode_all_memory_locations(memory_access_bits(spec));
+ }
+
+ uint64_t encoded = 0;
+ unsigned seen_locations = 0;
+ size_t pos = 0;
+ while (pos <= spec.size()) {
+ size_t comma = spec.find(',', pos);
+ std::string part = trim_copy(spec.substr(
+ pos, comma == std::string::npos ? std::string::npos : comma - pos));
+ if (part.empty())
+ throw LLVMAssertionError("empty memory effect component in: " + effects);
+
+ size_t colon = part.find(':');
+ if (colon == std::string::npos) {
+ throw LLVMAssertionError(
+ "memory location effects must use 'location: access': " + part);
+ }
+
+ std::string location = trim_copy(part.substr(0, colon));
+ std::string access_name = trim_copy(part.substr(colon + 1));
+ EncodedMemoryLocationEffect effect =
+ encode_memory_location_effect(location, memory_access_bits(access_name));
+ if (seen_locations & effect.seen_bit)
+ throw LLVMAssertionError("duplicate memory effect location: " + location);
+ seen_locations |= effect.seen_bit;
+ encoded |= effect.encoded;
+
+ if (comma == std::string::npos)
+ break;
+ pos = comma + 1;
+ }
+ return encoded;
+}
+
// =============================================================================
// Diagnostic Information
// =============================================================================
@@ -224,6 +370,92 @@ struct ValidityToken {
bool is_valid() const { return valid.load(); }
};
+struct ModuleTokenRegistry {
+ std::mutex mutex;
+ std::unordered_map> tokens;
+
+ static ModuleTokenRegistry &instance() {
+ static ModuleTokenRegistry registry;
+ return registry;
+ }
+
+ void register_module(LLVMModuleRef mod,
+ const std::shared_ptr &token) {
+ if (!mod || !token)
+ return;
+ std::lock_guard lock(mutex);
+ tokens[mod] = token;
+ }
+
+ void unregister_module(LLVMModuleRef mod) {
+ if (!mod)
+ return;
+ std::lock_guard lock(mutex);
+ tokens.erase(mod);
+ }
+
+ std::shared_ptr lookup(LLVMModuleRef mod) {
+ if (!mod)
+ return nullptr;
+ std::lock_guard lock(mutex);
+ auto it = tokens.find(mod);
+ if (it == tokens.end())
+ return nullptr;
+ return it->second.lock();
+ }
+};
+
+static LLVMModuleRef module_for_basic_block_if_parented(LLVMBasicBlockRef bb) {
+ if (!bb)
+ return nullptr;
+ LLVMValueRef fn = LLVMGetBasicBlockParent(bb);
+ if (!fn)
+ return nullptr;
+ return LLVMGetGlobalParent(fn);
+}
+
+static LLVMModuleRef module_for_value_if_parented(LLVMValueRef value) {
+ if (!value)
+ return nullptr;
+
+ if (LLVMIsAInstruction(value)) {
+ LLVMBasicBlockRef bb = LLVMGetInstructionParent(value);
+ return module_for_basic_block_if_parented(bb);
+ }
+
+ if (LLVMIsAArgument(value)) {
+ LLVMValueRef fn = LLVMGetParamParent(value);
+ if (!fn)
+ return nullptr;
+ return LLVMGetGlobalParent(fn);
+ }
+
+ if (LLVMValueIsBasicBlock(value)) {
+ LLVMBasicBlockRef bb = LLVMValueAsBasicBlock(value);
+ return module_for_basic_block_if_parented(bb);
+ }
+
+ if (LLVMIsAGlobalValue(value))
+ return LLVMGetGlobalParent(value);
+
+ return nullptr;
+}
+
+static std::shared_ptr module_token_for_module(
+ LLVMModuleRef mod) {
+ return ModuleTokenRegistry::instance().lookup(mod);
+}
+
+static std::shared_ptr module_token_for_basic_block(
+ LLVMBasicBlockRef bb) {
+ return module_token_for_module(module_for_basic_block_if_parented(bb));
+}
+
+static std::shared_ptr module_token_for_value(
+ LLVMValueRef value) {
+ return module_token_for_module(module_for_value_if_parented(value));
+}
+
// =============================================================================
// Base class to prevent copy
// =============================================================================
@@ -244,6 +476,11 @@ struct LLVMTypeWrapper;
struct LLVMValueWrapper;
struct LLVMFunctionWrapper;
struct LLVMAttributeAccessorWrapper;
+struct LLVMMetadataMapWrapper;
+struct LLVMNamedMetadataMapWrapper;
+struct LLVMNamedMetadataListWrapper;
+struct LLVMModuleFlagsWrapper;
+struct LLVMBuilderDebugLocationManager;
struct LLVMBasicBlockWrapper;
struct LLVMBuilderWrapper;
struct LLVMModuleManager;
@@ -251,6 +488,8 @@ struct LLVMBuilderManager;
struct LLVMDIBuilderManager;
struct LLVMTargetMachineWrapper;
struct LLVMPassBuilderOptionsWrapper;
+struct LLVMJITWrapper;
+struct LLVMCtypesFunctionWrapper;
struct LLVMNamedMDNodeWrapper;
struct LLVMOperandBundleWrapper;
struct LLVMUseWrapper;
@@ -263,11 +502,14 @@ struct LLVMDbgRecordWrapper;
struct LLVMOperandBundleWrapper {
LLVMOperandBundleRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::vector> m_module_tokens;
LLVMOperandBundleWrapper() = default;
- LLVMOperandBundleWrapper(LLVMOperandBundleRef ref,
- std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMOperandBundleWrapper(
+ LLVMOperandBundleRef ref, std::shared_ptr context_token,
+ std::vector> module_tokens = {})
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_tokens(std::move(module_tokens)) {}
~LLVMOperandBundleWrapper() {
if (m_ref) {
@@ -281,7 +523,8 @@ struct LLVMOperandBundleWrapper {
LLVMOperandBundleWrapper &
operator=(const LLVMOperandBundleWrapper &) = delete;
LLVMOperandBundleWrapper(LLVMOperandBundleWrapper &&other) noexcept
- : m_ref(other.m_ref), m_context_token(std::move(other.m_context_token)) {
+ : m_ref(other.m_ref), m_context_token(std::move(other.m_context_token)),
+ m_module_tokens(std::move(other.m_module_tokens)) {
other.m_ref = nullptr;
}
LLVMOperandBundleWrapper &
@@ -291,6 +534,7 @@ struct LLVMOperandBundleWrapper {
LLVMDisposeOperandBundle(m_ref);
m_ref = other.m_ref;
m_context_token = std::move(other.m_context_token);
+ m_module_tokens = std::move(other.m_module_tokens);
other.m_ref = nullptr;
}
return *this;
@@ -301,6 +545,10 @@ struct LLVMOperandBundleWrapper {
throw LLVMMemoryError("OperandBundle is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("OperandBundle used after context was destroyed");
+ for (const auto &token : m_module_tokens) {
+ if (token && !token->is_valid())
+ throw LLVMMemoryError("OperandBundle used after module was disposed");
+ }
}
std::string get_tag() const {
@@ -864,6 +1112,16 @@ struct LLVMTypeFactoryWrapper {
throw LLVMMemoryError("TypeFactory used after context was destroyed");
}
+ bool operator==(const LLVMTypeFactoryWrapper &other) const {
+ check_valid();
+ other.check_valid();
+ return m_ctx_ref == other.m_ctx_ref;
+ }
+
+ bool operator!=(const LLVMTypeFactoryWrapper &other) const {
+ return !(*this == other);
+ }
+
// =========================================================================
// Fixed-width integer types (properties)
// =========================================================================
@@ -1161,11 +1419,14 @@ struct LLVMTypeFactoryWrapper {
struct LLVMNamedMDNodeWrapper {
LLVMNamedMDNodeRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
LLVMNamedMDNodeWrapper() = default;
- LLVMNamedMDNodeWrapper(LLVMNamedMDNodeRef ref,
- std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMNamedMDNodeWrapper(
+ LLVMNamedMDNodeRef ref, std::shared_ptr context_token,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
bool operator==(const LLVMNamedMDNodeWrapper &other) const {
return m_ref == other.m_ref;
@@ -1179,6 +1440,8 @@ struct LLVMNamedMDNodeWrapper {
throw LLVMMemoryError("NamedMDNode is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("NamedMDNode used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("NamedMDNode used after module was disposed");
}
std::string get_name() const {
@@ -1193,7 +1456,7 @@ struct LLVMNamedMDNodeWrapper {
LLVMNamedMDNodeRef next_md = LLVMGetNextNamedMetadata(m_ref);
if (!next_md)
return std::nullopt;
- return LLVMNamedMDNodeWrapper(next_md, m_context_token);
+ return LLVMNamedMDNodeWrapper(next_md, m_context_token, m_module_token);
}
std::optional prev() {
@@ -1201,7 +1464,7 @@ struct LLVMNamedMDNodeWrapper {
LLVMNamedMDNodeRef prev_md = LLVMGetPreviousNamedMetadata(m_ref);
if (!prev_md)
return std::nullopt;
- return LLVMNamedMDNodeWrapper(prev_md, m_context_token);
+ return LLVMNamedMDNodeWrapper(prev_md, m_context_token, m_module_token);
}
};
@@ -1212,16 +1475,23 @@ struct LLVMNamedMDNodeWrapper {
struct LLVMUseWrapper {
LLVMUseRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
LLVMUseWrapper() = default;
- LLVMUseWrapper(LLVMUseRef ref, std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMUseWrapper(LLVMUseRef ref, std::shared_ptr context_token,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_token(module_token ? std::move(module_token)
+ : module_token_for_value(
+ ref ? LLVMGetUser(ref) : nullptr)) {}
void check_valid() const {
if (!m_ref)
throw LLVMMemoryError("Use is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("Use used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("Use used after module was disposed");
}
// get_user, get_used_value, and get_operand_index are implemented after
@@ -1242,11 +1512,14 @@ struct LLVMUseWrapper {
struct LLVMDbgRecordWrapper {
LLVMDbgRecordRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
LLVMDbgRecordWrapper() = default;
- LLVMDbgRecordWrapper(LLVMDbgRecordRef ref,
- std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMDbgRecordWrapper(
+ LLVMDbgRecordRef ref, std::shared_ptr context_token,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
bool operator==(const LLVMDbgRecordWrapper &other) const {
return m_ref == other.m_ref;
@@ -1260,6 +1533,8 @@ struct LLVMDbgRecordWrapper {
throw LLVMMemoryError("DbgRecord is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("DbgRecord used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("DbgRecord used after module was disposed");
}
std::optional next() const {
@@ -1267,7 +1542,7 @@ struct LLVMDbgRecordWrapper {
LLVMDbgRecordRef next_ref = LLVMGetNextDbgRecord(m_ref);
if (!next_ref)
return std::nullopt;
- return LLVMDbgRecordWrapper(next_ref, m_context_token);
+ return LLVMDbgRecordWrapper(next_ref, m_context_token, m_module_token);
}
std::optional prev() const {
@@ -1275,7 +1550,7 @@ struct LLVMDbgRecordWrapper {
LLVMDbgRecordRef prev_ref = LLVMGetPreviousDbgRecord(m_ref);
if (!prev_ref)
return std::nullopt;
- return LLVMDbgRecordWrapper(prev_ref, m_context_token);
+ return LLVMDbgRecordWrapper(prev_ref, m_context_token, m_module_token);
}
};
@@ -1286,10 +1561,14 @@ struct LLVMDbgRecordWrapper {
struct LLVMValueWrapper {
LLVMValueRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
LLVMValueWrapper() = default;
- LLVMValueWrapper(LLVMValueRef ref, std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMValueWrapper(LLVMValueRef ref, std::shared_ptr context_token,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_token(module_token ? std::move(module_token)
+ : module_token_for_value(ref)) {}
bool operator==(const LLVMValueWrapper &other) const {
return m_ref == other.m_ref;
@@ -1303,6 +1582,8 @@ struct LLVMValueWrapper {
throw LLVMMemoryError("Value is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("Value used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("Value used after module was disposed");
}
static const char *opcode_name(LLVMOpcode op) {
@@ -2816,7 +3097,11 @@ struct LLVMValueWrapper {
throw LLVMAssertionError("get_operand_bundle_at_index: operand bundle at index " +
std::to_string(index) + " is null");
}
- return LLVMOperandBundleWrapper(bundle, m_context_token);
+ std::vector> module_tokens;
+ if (m_module_token)
+ module_tokens.push_back(m_module_token);
+ return LLVMOperandBundleWrapper(bundle, m_context_token,
+ std::move(module_tokens));
}
// Get indices for extractvalue/insertvalue
@@ -3249,6 +3534,19 @@ struct LLVMValueWrapper {
// LLVMMetadataWrapper
LLVMMetadataWrapper as_metadata() const;
+ LLVMMetadataMapWrapper metadata() const;
+
+ LLVMTypeFactoryWrapper types() const {
+ check_valid();
+ LLVMTypeRef ty = LLVMTypeOf(m_ref);
+ if (!ty)
+ throw LLVMAssertionError("Value has no type context");
+ LLVMContextRef ctx_ref = LLVMGetTypeContext(ty);
+ if (!ctx_ref)
+ throw LLVMAssertionError("Value has no type context");
+ return LLVMTypeFactoryWrapper(ctx_ref, m_context_token);
+ }
+
// Unified set_metadata - works for both instructions and globals
// Declared here, implemented after LLVMMetadataWrapper
// Takes a context for converting metadata to value (needed for instructions)
@@ -3275,7 +3573,7 @@ struct LLVMValueWrapper {
LLVMDbgRecordRef ref = LLVMGetFirstDbgRecord(m_ref);
if (!ref)
return std::nullopt;
- return LLVMDbgRecordWrapper(ref, m_context_token);
+ return LLVMDbgRecordWrapper(ref, m_context_token, m_module_token);
}
std::optional last_dbg_record() const {
@@ -3286,7 +3584,7 @@ struct LLVMValueWrapper {
LLVMDbgRecordRef ref = LLVMGetLastDbgRecord(m_ref);
if (!ref)
return std::nullopt;
- return LLVMDbgRecordWrapper(ref, m_context_token);
+ return LLVMDbgRecordWrapper(ref, m_context_token, m_module_token);
}
// Create a builder positioned before this instruction
@@ -3300,11 +3598,15 @@ struct LLVMValueWrapper {
struct LLVMBasicBlockWrapper {
LLVMBasicBlockRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
LLVMBasicBlockWrapper() = default;
- LLVMBasicBlockWrapper(LLVMBasicBlockRef ref,
- std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMBasicBlockWrapper(
+ LLVMBasicBlockRef ref, std::shared_ptr context_token,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_token(module_token ? std::move(module_token)
+ : module_token_for_basic_block(ref)) {}
bool operator==(const LLVMBasicBlockWrapper &other) const {
return m_ref == other.m_ref;
@@ -3318,6 +3620,8 @@ struct LLVMBasicBlockWrapper {
throw LLVMMemoryError("BasicBlock is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("BasicBlock used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("BasicBlock used after module was disposed");
}
std::string get_name() const {
@@ -3746,6 +4050,17 @@ struct LLVMBasicBlockWrapper {
return result;
}
+ LLVMTypeFactoryWrapper types() const {
+ check_valid();
+ LLVMTypeRef ty = LLVMTypeOf(LLVMBasicBlockAsValue(m_ref));
+ if (!ty)
+ throw LLVMAssertionError("BasicBlock has no type context");
+ LLVMContextRef ctx_ref = LLVMGetTypeContext(ty);
+ if (!ctx_ref)
+ throw LLVMAssertionError("BasicBlock has no type context");
+ return LLVMTypeFactoryWrapper(ctx_ref, m_context_token);
+ }
+
// Create a builder positioned at the end of this basic block, or before the
// first non-PHI instruction when requested.
LLVMBuilderManager *create_builder(bool first_non_phi = false) const;
@@ -3757,8 +4072,11 @@ struct LLVMBasicBlockWrapper {
struct LLVMFunctionWrapper : LLVMValueWrapper {
LLVMFunctionWrapper() = default;
- LLVMFunctionWrapper(LLVMValueRef ref, std::shared_ptr token)
- : LLVMValueWrapper(ref, std::move(token)) {}
+ LLVMFunctionWrapper(
+ LLVMValueRef ref, std::shared_ptr context_token,
+ std::shared_ptr module_token = nullptr)
+ : LLVMValueWrapper(ref, std::move(context_token),
+ std::move(module_token)) {}
unsigned param_count() const {
check_valid();
@@ -3886,6 +4204,7 @@ struct LLVMFunctionWrapper : LLVMValueWrapper {
"append_existing_basic_block requires an unattached basic block");
}
LLVMAppendExistingBasicBlock(m_ref, bb.m_ref);
+ const_cast(bb).m_module_token = m_module_token;
}
void erase() {
@@ -3951,6 +4270,12 @@ struct LLVMFunctionWrapper : LLVMValueWrapper {
// Get the context this function belongs to (derived via module)
LLVMContextWrapper *context() const;
+ LLVMTypeFactoryWrapper types() const { return LLVMValueWrapper::types(); }
+
+ // Create a builder positioned at the end of this function's entry block.
+ // If the function has no blocks yet, create an entry block first.
+ LLVMBuilderManager *create_builder(bool first_non_phi = false) const;
+
// =========================================================================
// Function verification (Analysis.h)
// =========================================================================
@@ -3963,6 +4288,9 @@ struct LLVMFunctionWrapper : LLVMValueWrapper {
return !LLVMVerifyFunction(m_ref, LLVMReturnStatusAction);
}
+ void optimize(const std::string &pipeline, LLVMTargetMachineWrapper *tm,
+ LLVMPassBuilderOptionsWrapper *opts);
+
/// Verify this function and print any errors to stderr.
/// Wraps LLVMVerifyFunction with LLVMPrintMessageAction.
bool verify_and_print() const {
@@ -4058,16 +4386,19 @@ enum class LLVMAttributeAccessorTarget { Function, CallSite };
struct LLVMAttributeAccessorWrapper {
LLVMValueRef m_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
unsigned m_idx = LLVMAttributeReturnIndex;
LLVMAttributeAccessorTarget m_target = LLVMAttributeAccessorTarget::Function;
LLVMAttributeAccessorWrapper() = default;
- LLVMAttributeAccessorWrapper(LLVMValueRef ref,
- std::shared_ptr token,
- unsigned idx,
- LLVMAttributeAccessorTarget target)
- : m_ref(ref), m_context_token(std::move(token)), m_idx(idx),
- m_target(target) {}
+ LLVMAttributeAccessorWrapper(
+ LLVMValueRef ref, std::shared_ptr context_token,
+ unsigned idx, LLVMAttributeAccessorTarget target,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_context_token(std::move(context_token)),
+ m_module_token(module_token ? std::move(module_token)
+ : module_token_for_value(ref)),
+ m_idx(idx), m_target(target) {}
static unsigned normalize_function_index(LLVMValueRef fn,
const char *api_name, int idx) {
@@ -4152,6 +4483,8 @@ struct LLVMAttributeAccessorWrapper {
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError(
"AttributeAccessor used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("AttributeAccessor used after module was disposed");
if (m_target == LLVMAttributeAccessorTarget::Function) {
if (!LLVMIsAFunction(m_ref)) {
throw LLVMAssertionError(
@@ -4262,6 +4595,10 @@ struct LLVMAttributeAccessorWrapper {
add(wrapped);
}
+ void add_memory(const std::string &effects = "none") {
+ add("memory", encode_memory_effects(effects));
+ }
+
void add_type(const std::string &name, const LLVMTypeWrapper &type) {
check_valid();
type.check_valid();
@@ -4452,7 +4789,7 @@ inline LLVMValueWrapper LLVMUseWrapper::get_user() const {
LLVMValueRef user = LLVMGetUser(m_ref);
if (!user)
throw LLVMAssertionError("Use has no user");
- return LLVMValueWrapper(user, m_context_token);
+ return LLVMValueWrapper(user, m_context_token, m_module_token);
}
inline LLVMValueWrapper LLVMUseWrapper::get_used_value() const {
@@ -4460,7 +4797,7 @@ inline LLVMValueWrapper LLVMUseWrapper::get_used_value() const {
LLVMValueRef used = LLVMGetUsedValue(m_ref);
if (!used)
throw LLVMAssertionError("Use has no used value");
- return LLVMValueWrapper(used, m_context_token);
+ return LLVMValueWrapper(used, m_context_token, m_module_token);
}
// Implementation of LLVMUseWrapper::get_operand_index
@@ -4778,6 +5115,12 @@ struct LLVMBuilderWrapper : NoMoveCopy {
LLVMModuleWrapper *module() const;
LLVMContextWrapper *context() const;
+ LLVMBuilderDebugLocationManager *debug_location(
+ const LLVMMetadataWrapper &loc) const;
+ LLVMBuilderDebugLocationManager *debug_location(
+ unsigned line, unsigned column, const LLVMMetadataWrapper &scope,
+ const LLVMMetadataWrapper *inlined_at) const;
+
void position_at(const LLVMBasicBlockWrapper &bb,
const LLVMValueWrapper &inst) {
check_valid();
@@ -5135,14 +5478,14 @@ struct LLVMBuilderWrapper : NoMoveCopy {
m_context_token);
}
- LLVMValueWrapper build_array_alloca(const LLVMTypeWrapper &ty,
- const LLVMValueWrapper &size,
- const std::string &name = "") {
+ LLVMValueWrapper build_alloca(const LLVMTypeWrapper &ty,
+ const LLVMValueWrapper &count,
+ const std::string &name = "") {
check_valid();
ty.check_valid();
- size.check_valid();
+ count.check_valid();
return LLVMValueWrapper(
- LLVMBuildArrayAlloca(m_ref, ty.m_ref, size.m_ref, name.c_str()),
+ LLVMBuildArrayAlloca(m_ref, ty.m_ref, count.m_ref, name.c_str()),
m_context_token);
}
@@ -5572,6 +5915,70 @@ struct LLVMBuilderWrapper : NoMoveCopy {
m_context_token);
}
+ LLVMValueWrapper intrinsic(const std::string &intrinsic_name,
+ const Iterable &args,
+ const Iterable &overloaded_types,
+ const std::string &name_hint = "") {
+ check_valid();
+ if (intrinsic_name.empty())
+ throw LLVMAssertionError("intrinsic name cannot be empty");
+
+ unsigned id =
+ LLVMLookupIntrinsicID(intrinsic_name.c_str(), intrinsic_name.size());
+ if (id == 0) {
+ throw LLVMAssertionError("Unknown LLVM intrinsic: " + intrinsic_name);
+ }
+ if (LLVMIntrinsicIsOverloaded(id) && overloaded_types.empty()) {
+ throw LLVMAssertionError("Intrinsic '" + intrinsic_name +
+ "' is overloaded; pass overloaded_types=[...]");
+ }
+
+ LLVMBasicBlockRef bb = LLVMGetInsertBlock(m_ref);
+ if (!bb)
+ throw LLVMAssertionError("intrinsic requires a builder insertion block");
+ LLVMValueRef parent_fn = LLVMGetBasicBlockParent(bb);
+ if (!parent_fn)
+ throw LLVMAssertionError(
+ "intrinsic requires an insertion block in a function");
+ LLVMModuleRef mod_ref = LLVMGetGlobalParent(parent_fn);
+ if (!mod_ref)
+ throw LLVMAssertionError(
+ "intrinsic requires an insertion function in a module");
+
+ std::vector type_refs;
+ type_refs.reserve(overloaded_types.size());
+ for (const auto &ty : overloaded_types) {
+ ty.check_valid();
+ type_refs.push_back(ty.m_ref);
+ }
+
+ LLVMValueRef decl = LLVMGetIntrinsicDeclaration(
+ mod_ref, id, type_refs.data(), type_refs.size());
+ if (!decl) {
+ throw LLVMError("Failed to get declaration for intrinsic '" +
+ intrinsic_name + "'");
+ }
+
+ std::vector arg_refs;
+ arg_refs.reserve(args.size());
+ for (const auto &arg : args) {
+ arg.check_valid();
+ arg_refs.push_back(arg.m_ref);
+ }
+
+ LLVMTypeRef func_ty = LLVMGlobalGetValueType(decl);
+ LLVMTypeRef ret_ty = LLVMGetReturnType(func_ty);
+ if (LLVMGetTypeKind(ret_ty) == LLVMVoidTypeKind && !name_hint.empty()) {
+ throw LLVMAssertionError("Cannot name call to intrinsic returning void");
+ }
+
+ return LLVMValueWrapper(
+ LLVMBuildCall2(m_ref, func_ty, decl, arg_refs.data(),
+ static_cast(arg_refs.size()),
+ name_hint.c_str()),
+ m_context_token);
+ }
+
LLVMValueWrapper unreachable() {
check_valid();
return LLVMValueWrapper(LLVMBuildUnreachable(m_ref), m_context_token);
@@ -6027,13 +6434,16 @@ struct LLVMModuleWrapper : NoMoveCopy {
: m_context_token(std::move(context_token)),
m_token(std::make_shared()), m_ctx_ref(ctx) {
m_ref = LLVMModuleCreateWithNameInContext(name.c_str(), ctx);
+ ModuleTokenRegistry::instance().register_module(m_ref, m_token);
}
// Constructor for wrapping an existing module (from bitcode parsing)
LLVMModuleWrapper(LLVMModuleRef mod, LLVMContextRef ctx,
std::shared_ptr context_token)
: m_ref(mod), m_context_token(std::move(context_token)),
- m_token(std::make_shared()), m_ctx_ref(ctx) {}
+ m_token(std::make_shared()), m_ctx_ref(ctx) {
+ ModuleTokenRegistry::instance().register_module(m_ref, m_token);
+ }
// Constructor for non-owning (borrowed) reference to an existing module.
// Used by function.module to return the function's parent module.
@@ -6045,6 +6455,8 @@ struct LLVMModuleWrapper : NoMoveCopy {
auto *wrapper = new LLVMModuleWrapper();
wrapper->m_ref = mod;
wrapper->m_context_token = std::move(context_token);
+ if (!module_token)
+ module_token = module_token_for_module(mod);
wrapper->m_token = module_token ? std::move(module_token)
: std::make_shared();
wrapper->m_ctx_ref = ctx;
@@ -6054,6 +6466,7 @@ struct LLVMModuleWrapper : NoMoveCopy {
~LLVMModuleWrapper() {
if (m_ref && !m_borrowed) {
+ ModuleTokenRegistry::instance().unregister_module(m_ref);
// Only dispose the module if its context is still alive.
// If the context was already destroyed, the module memory is already
// freed and calling LLVMDisposeModule would cause a use-after-free crash.
@@ -6088,10 +6501,19 @@ struct LLVMModuleWrapper : NoMoveCopy {
void dispose() {
if (m_ref) {
- LLVMDisposeModule(m_ref);
+ if (!m_borrowed) {
+ ModuleTokenRegistry::instance().unregister_module(m_ref);
+ if (m_context_token && m_context_token->is_valid()) {
+ LLVMDisposeModule(m_ref);
+ } else {
+ fprintf(stderr, "Warning: LLVM Module outlived its Context. "
+ "This may cause a memory leak. "
+ "Ensure modules are deleted before their context.\n");
+ }
+ }
m_ref = nullptr;
}
- if (m_token) {
+ if (m_token && !m_borrowed) {
m_token->invalidate();
}
}
@@ -6387,6 +6809,7 @@ struct LLVMModuleWrapper : NoMoveCopy {
throw LLVMError("Failed to link modules");
}
// LLVMLinkModules2 destroys the source module, so mark it as disposed
+ ModuleTokenRegistry::instance().unregister_module(src.m_ref);
src.m_ref = nullptr;
if (src.m_token) {
src.m_token->invalidate();
@@ -6539,7 +6962,7 @@ struct LLVMModuleWrapper : NoMoveCopy {
LLVMNamedMDNodeRef md = LLVMGetFirstNamedMetadata(m_ref);
if (!md)
return std::nullopt;
- return LLVMNamedMDNodeWrapper(md, m_context_token);
+ return LLVMNamedMDNodeWrapper(md, m_context_token, m_token);
}
std::optional last_named_metadata() {
@@ -6547,7 +6970,7 @@ struct LLVMModuleWrapper : NoMoveCopy {
LLVMNamedMDNodeRef md = LLVMGetLastNamedMetadata(m_ref);
if (!md)
return std::nullopt;
- return LLVMNamedMDNodeWrapper(md, m_context_token);
+ return LLVMNamedMDNodeWrapper(md, m_context_token, m_token);
}
std::optional
@@ -6557,14 +6980,14 @@ struct LLVMModuleWrapper : NoMoveCopy {
LLVMGetNamedMetadata(m_ref, name.c_str(), name.size());
if (!md)
return std::nullopt;
- return LLVMNamedMDNodeWrapper(md, m_context_token);
+ return LLVMNamedMDNodeWrapper(md, m_context_token, m_token);
}
LLVMNamedMDNodeWrapper add_named_metadata(const std::string &name) {
check_valid();
return LLVMNamedMDNodeWrapper(
LLVMGetOrInsertNamedMetadata(m_ref, name.c_str(), name.size()),
- m_context_token);
+ m_context_token, m_token);
}
unsigned get_named_metadata_num_operands(const std::string &name) {
@@ -6619,6 +7042,14 @@ struct LLVMModuleWrapper : NoMoveCopy {
/// Get a module-level flag by key.
std::optional get_module_flag(const std::string &key);
+ LLVMNamedMetadataMapWrapper named_metadata() const;
+ LLVMModuleFlagsWrapper module_flags() const;
+
+ LLVMTypeFactoryWrapper types() const {
+ check_valid();
+ return LLVMTypeFactoryWrapper(m_ctx_ref, m_context_token);
+ }
+
// =========================================================================
// Module API Refactor: Methods moved from global functions
// =========================================================================
@@ -6666,6 +7097,12 @@ struct LLVMModuleWrapper : NoMoveCopy {
void run_passes(const std::string &passes, LLVMTargetMachineWrapper *tm,
LLVMPassBuilderOptionsWrapper *opts);
+ void optimize(const std::string &pipeline, LLVMTargetMachineWrapper *tm,
+ LLVMPassBuilderOptionsWrapper *opts);
+
+ nb::bytes emit_object(LLVMTargetMachineWrapper *tm = nullptr);
+ nb::bytes emit_assembly(LLVMTargetMachineWrapper *tm = nullptr);
+
// Clone - returns a ModuleManager that must be used with 'with' or .dispose()
LLVMModuleManager *clone() const;
@@ -7070,6 +7507,9 @@ struct LLVMContextWrapper : NoMoveCopy {
LLVMMetadataWrapper create_debug_location(
unsigned line, unsigned column, const LLVMMetadataWrapper &scope,
const LLVMMetadataWrapper *inlined_at);
+ LLVMMetadataWrapper debug_location(unsigned line, unsigned column,
+ const LLVMMetadataWrapper &scope,
+ const LLVMMetadataWrapper *inlined_at);
// Module creation (returns context manager) - defined after LLVMModuleManager
LLVMModuleManager *create_module(const std::string &name);
@@ -7092,18 +7532,25 @@ struct LLVMContextWrapper : NoMoveCopy {
struct LLVMContextManager : NoMoveCopy {
std::unique_ptr m_context;
+ bool m_entered = false;
+ bool m_disposed = false;
LLVMContextWrapper &enter() {
- if (m_context)
+ if (m_entered)
throw LLVMMemoryError("Context manager already entered");
+ if (m_disposed)
+ throw LLVMMemoryError("Context manager has already exited");
m_context = std::make_unique();
+ m_entered = true;
return *m_context;
}
void exit(const nb::object &, const nb::object &, const nb::object &) {
- if (!m_context)
+ if (!m_entered || !m_context)
throw LLVMMemoryError("Context manager not entered");
- m_context.reset();
+ m_context->dispose();
+ m_entered = false;
+ m_disposed = true;
}
};
@@ -7160,7 +7607,7 @@ struct LLVMModuleManager : NoMoveCopy {
throw LLVMMemoryError("Module has already been disposed");
if (!m_entered)
throw LLVMMemoryError("Module manager was not entered");
- m_module.reset();
+ m_module->dispose();
m_disposed = true;
}
@@ -7239,7 +7686,7 @@ struct LLVMBuilderManager : NoMoveCopy {
throw LLVMMemoryError("Builder has already been disposed");
if (!m_entered)
throw LLVMMemoryError("Builder manager was not entered");
- m_builder.reset();
+ m_builder->dispose();
m_disposed = true;
}
@@ -7268,7 +7715,12 @@ LLVMBuilderManager *
LLVMContextWrapper::create_builder(const LLVMBasicBlockWrapper &bb) {
check_valid();
bb.check_valid();
+ if (bb.m_context_token != m_token) {
+ throw LLVMAssertionError(
+ "Context.create_builder requires a basic block from this context");
+ }
auto manager = new LLVMBuilderManager(this);
+ manager->m_module_token = bb.m_module_token;
manager->m_initial_bb = bb.m_ref;
return manager;
}
@@ -7283,7 +7735,12 @@ LLVMContextWrapper::create_builder(const LLVMValueWrapper &inst,
if (!LLVMGetInstructionParent(inst.m_ref))
throw LLVMAssertionError(
"create_builder requires an instruction in a basic block");
+ if (inst.m_context_token != m_token) {
+ throw LLVMAssertionError(
+ "Context.create_builder requires an instruction from this context");
+ }
auto manager = new LLVMBuilderManager(this);
+ manager->m_module_token = inst.m_module_token;
manager->m_initial_inst = inst.m_ref;
manager->m_before_dbg = before_dbg;
return manager;
@@ -7302,6 +7759,7 @@ LLVMBuilderManager *LLVMBasicBlockWrapper::create_builder(
LLVMContextWrapper *ctx =
const_cast(this)->context();
auto manager = new LLVMBuilderManager(ctx);
+ manager->m_module_token = m_module_token;
if (first_non_phi) {
LLVMValueRef inst = LLVMGetFirstInstruction(m_ref);
@@ -7320,6 +7778,25 @@ LLVMBuilderManager *LLVMBasicBlockWrapper::create_builder(
return manager;
}
+LLVMBuilderManager *LLVMFunctionWrapper::create_builder(
+ bool first_non_phi) const {
+ check_valid();
+ LLVMModuleRef mod_ref = LLVMGetGlobalParent(m_ref);
+ if (!mod_ref)
+ throw LLVMAssertionError("create_builder requires a function in a module");
+
+ LLVMBasicBlockRef entry = nullptr;
+ if (LLVMCountBasicBlocks(m_ref) == 0) {
+ LLVMContextRef ctx_ref = LLVMGetModuleContext(mod_ref);
+ entry = LLVMAppendBasicBlockInContext(ctx_ref, m_ref, "entry");
+ } else {
+ entry = LLVMGetEntryBasicBlock(m_ref);
+ }
+
+ LLVMBasicBlockWrapper entry_wrapper(entry, m_context_token, m_module_token);
+ return entry_wrapper.create_builder(first_non_phi);
+}
+
LLVMBuilderManager *LLVMValueWrapper::create_builder(bool before_dbg) const {
check_valid();
if (!is_a_instruction())
@@ -7330,6 +7807,7 @@ LLVMBuilderManager *LLVMValueWrapper::create_builder(bool before_dbg) const {
// Get the context for this value
LLVMContextWrapper *ctx = get_context();
auto manager = new LLVMBuilderManager(ctx);
+ manager->m_module_token = m_module_token;
manager->m_initial_inst = m_ref;
manager->m_before_dbg = before_dbg;
return manager;
@@ -7467,6 +7945,8 @@ LLVMModuleManager *LLVMModuleWrapper::clone() const {
raw_wrapper->m_context_token = m_context_token;
raw_wrapper->m_token = std::make_shared();
raw_wrapper->m_ctx_ref = m_ctx_ref;
+ ModuleTokenRegistry::instance().register_module(cloned_ref,
+ raw_wrapper->m_token);
// Wrap in unique_ptr and return a manager that owns the cloned module
return new LLVMModuleManager(std::unique_ptr(raw_wrapper));
@@ -8010,27 +8490,6 @@ struct LLVMTargetWrapper {
}
};
-// Target initialization functions
-void initialize_all_target_infos() { LLVMInitializeAllTargetInfos(); }
-
-void initialize_all_targets() { LLVMInitializeAllTargets(); }
-
-void initialize_all_target_mcs() { LLVMInitializeAllTargetMCs(); }
-
-void initialize_all_asm_printers() { LLVMInitializeAllAsmPrinters(); }
-
-void initialize_all_asm_parsers() { LLVMInitializeAllAsmParsers(); }
-
-void initialize_all_disassemblers() { LLVMInitializeAllDisassemblers(); }
-
-// Convenience: initialize all targets, MCs, asm printers, and asm parsers
-void initialize_all() {
- LLVMInitializeAllTargetInfos();
- LLVMInitializeAllTargets();
- LLVMInitializeAllTargetMCs();
- LLVMInitializeAllAsmPrinters();
- LLVMInitializeAllAsmParsers();
-}
std::optional get_first_target() {
LLVMTargetRef ref = LLVMGetFirstTarget();
@@ -8094,21 +8553,6 @@ std::string get_host_cpu_features() {
return result;
}
-// Native target initialization
-bool initialize_native_target() { return LLVMInitializeNativeTarget() == 0; }
-
-bool initialize_native_asm_printer() {
- return LLVMInitializeNativeAsmPrinter() == 0;
-}
-
-bool initialize_native_asm_parser() {
- return LLVMInitializeNativeAsmParser() == 0;
-}
-
-bool initialize_native_disassembler() {
- return LLVMInitializeNativeDisassembler() == 0;
-}
-
// =============================================================================
// Target Data Wrapper
// =============================================================================
@@ -8362,6 +8806,46 @@ LLVMTargetMachineWrapper *create_target_machine(const LLVMTargetWrapper &target,
return new LLVMTargetMachineWrapper(ref);
}
+LLVMTargetMachineWrapper *create_host_target_machine(
+ const std::string &cpu = "", const std::string &features = "",
+ LLVMCodeGenOptLevel opt_level = LLVMCodeGenLevelDefault,
+ LLVMRelocMode reloc_mode = LLVMRelocDefault,
+ LLVMCodeModel code_model = LLVMCodeModelDefault) {
+ if (LLVMInitializeNativeTarget() != 0)
+ throw LLVMError("Failed to initialize native target");
+ if (LLVMInitializeNativeAsmPrinter() != 0)
+ throw LLVMError("Failed to initialize native ASM printer");
+
+ char *triple_msg = LLVMGetDefaultTargetTriple();
+ std::string triple = triple_msg ? triple_msg : "";
+ if (triple_msg)
+ LLVMDisposeMessage(triple_msg);
+ if (triple.empty())
+ throw LLVMError("Failed to determine default target triple");
+
+ LLVMTargetRef target_ref = nullptr;
+ char *error = nullptr;
+ if (LLVMGetTargetFromTriple(triple.c_str(), &target_ref, &error)) {
+ std::string msg = error ? error : "Unknown error";
+ if (error)
+ LLVMDisposeMessage(error);
+ throw LLVMError("Failed to get host target from triple '" + triple +
+ "': " + msg);
+ }
+ if (error)
+ LLVMDisposeMessage(error);
+ if (!target_ref)
+ throw LLVMError("Failed to get host target from triple '" + triple + "'");
+
+ LLVMTargetMachineRef ref = LLVMCreateTargetMachine(
+ target_ref, triple.c_str(), cpu.c_str(), features.c_str(), opt_level,
+ reloc_mode, code_model);
+ if (!ref)
+ throw LLVMError("Failed to create host target machine for triple '" +
+ triple + "'");
+ return new LLVMTargetMachineWrapper(ref);
+}
+
// =============================================================================
// Pass Builder Options Wrapper
// =============================================================================
@@ -8455,12 +8939,21 @@ void run_passes(LLVMModuleWrapper &mod, const std::string &passes,
tm_ref = tm->m_ref;
}
LLVMPassBuilderOptionsRef opts_ref = nullptr;
+ LLVMPassBuilderOptionsRef owned_opts = nullptr;
if (opts) {
opts->check_valid();
opts_ref = opts->m_ref;
+ } else {
+ // LLVMRunPasses expects a valid options object on some LLVM builds.
+ owned_opts = LLVMCreatePassBuilderOptions();
+ if (!owned_opts)
+ throw LLVMError("Failed to create pass builder options");
+ opts_ref = owned_opts;
}
LLVMErrorRef err = LLVMRunPasses(mod.m_ref, passes.c_str(), tm_ref, opts_ref);
+ if (owned_opts)
+ LLVMDisposePassBuilderOptions(owned_opts);
if (err) {
char *msg = LLVMGetErrorMessage(err);
std::string error_msg = msg ? msg : "Unknown error";
@@ -8475,6 +8968,399 @@ inline void LLVMModuleWrapper::run_passes(const std::string &passes,
::run_passes(*this, passes, tm, opts);
}
+void run_passes_on_function(LLVMFunctionWrapper &fn, const std::string &passes,
+ LLVMTargetMachineWrapper *tm,
+ LLVMPassBuilderOptionsWrapper *opts) {
+ fn.check_valid();
+ if (LLVMIsDeclaration(fn.m_ref)) {
+ throw LLVMAssertionError(
+ "Function.optimize requires a function definition (check "
+ "is_declaration first)");
+ }
+
+ LLVMTargetMachineRef tm_ref = nullptr;
+ if (tm) {
+ tm->check_valid();
+ tm_ref = tm->m_ref;
+ }
+
+ LLVMPassBuilderOptionsRef opts_ref = nullptr;
+ LLVMPassBuilderOptionsRef owned_opts = nullptr;
+ if (opts) {
+ opts->check_valid();
+ opts_ref = opts->m_ref;
+ } else {
+ // LLVMRunPassesOnFunction expects a valid options object on some LLVM
+ // builds.
+ owned_opts = LLVMCreatePassBuilderOptions();
+ if (!owned_opts)
+ throw LLVMError("Failed to create pass builder options");
+ opts_ref = owned_opts;
+ }
+
+ LLVMErrorRef err = LLVMRunPassesOnFunction(fn.m_ref, passes.c_str(), tm_ref,
+ opts_ref);
+ if (owned_opts)
+ LLVMDisposePassBuilderOptions(owned_opts);
+ if (err) {
+ char *msg = LLVMGetErrorMessage(err);
+ std::string error_msg = msg ? msg : "Unknown error";
+ LLVMDisposeErrorMessage(msg);
+ throw LLVMError("Failed to run function passes: " + error_msg);
+ }
+}
+
+inline void LLVMFunctionWrapper::optimize(
+ const std::string &pipeline, LLVMTargetMachineWrapper *tm,
+ LLVMPassBuilderOptionsWrapper *opts) {
+ try {
+ ::run_passes_on_function(*this, pipeline, tm, opts);
+ } catch (const LLVMError &e) {
+ throw LLVMError("Failed to optimize function with pipeline '" + pipeline +
+ "': " + e.what());
+ }
+}
+
+inline void LLVMModuleWrapper::optimize(const std::string &pipeline,
+ LLVMTargetMachineWrapper *tm,
+ LLVMPassBuilderOptionsWrapper *opts) {
+ try {
+ ::run_passes(*this, pipeline, tm, opts);
+ } catch (const LLVMError &e) {
+ throw LLVMError("Failed to optimize with pipeline '" + pipeline +
+ "': " + e.what());
+ }
+}
+
+inline nb::bytes LLVMModuleWrapper::emit_object(LLVMTargetMachineWrapper *tm) {
+ check_valid();
+ if (tm) {
+ tm->check_valid();
+ return tm->emit_to_memory_buffer(*this, LLVMObjectFile);
+ }
+ std::unique_ptr host_tm(create_host_target_machine());
+ return host_tm->emit_to_memory_buffer(*this, LLVMObjectFile);
+}
+
+inline nb::bytes LLVMModuleWrapper::emit_assembly(LLVMTargetMachineWrapper *tm) {
+ check_valid();
+ if (tm) {
+ tm->check_valid();
+ return tm->emit_to_memory_buffer(*this, LLVMAssemblyFile);
+ }
+ std::unique_ptr host_tm(create_host_target_machine());
+ return host_tm->emit_to_memory_buffer(*this, LLVMAssemblyFile);
+}
+
+// =============================================================================
+// ORC LLJIT Wrapper
+// =============================================================================
+
+struct LLVMCtypesFunctionWrapper : NoMoveCopy {
+ nb::object m_func;
+ std::shared_ptr m_jit_token;
+
+ LLVMCtypesFunctionWrapper(nb::object func,
+ std::shared_ptr jit_token)
+ : m_func(std::move(func)), m_jit_token(std::move(jit_token)) {}
+
+ nb::object call(const nb::args &args) const {
+ if (!m_jit_token || !m_jit_token->is_valid()) {
+ throw LLVMMemoryError("JIT function called after JIT was disposed");
+ }
+ PyObject *result = PyObject_CallObject(m_func.ptr(), args.ptr());
+ if (!result)
+ throw nb::python_error();
+ return nb::steal(result);
+ }
+
+ nb::object ctypes_callable() const { return m_func; }
+};
+
+static uint64_t python_address_from_object(const nb::object &value) {
+ if (PyLong_Check(value.ptr())) {
+ unsigned long long address = PyLong_AsUnsignedLongLong(value.ptr());
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ throw LLVMAssertionError(
+ "symbol address must be a non-negative integer or ctypes object");
+ }
+ return static_cast(address);
+ }
+
+ nb::object ctypes = nb::module_::import_("ctypes");
+ nb::object c_void_p = ctypes.attr("c_void_p");
+ nb::object cast = ctypes.attr("cast");
+ nb::object address_obj = cast(value, c_void_p).attr("value");
+ if (address_obj.is_none()) {
+ throw LLVMAssertionError(
+ "ctypes object could not be converted to a non-null address");
+ }
+ unsigned long long address = PyLong_AsUnsignedLongLong(address_obj.ptr());
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ throw LLVMAssertionError(
+ "ctypes object address did not fit in an unsigned 64-bit integer");
+ }
+ return static_cast(address);
+}
+
+struct LLVMJITWrapper : NoMoveCopy {
+ LLVMOrcLLJITRef m_ref = nullptr;
+ std::shared_ptr m_token = std::make_shared();
+ std::vector m_pinned_symbols;
+
+ LLVMJITWrapper() = default;
+ explicit LLVMJITWrapper(LLVMOrcLLJITRef ref) : m_ref(ref) {}
+
+ ~LLVMJITWrapper() {
+ m_pinned_symbols.clear();
+ if (m_ref) {
+ LLVMErrorRef err = LLVMOrcDisposeLLJIT(m_ref);
+ if (err) {
+ // Destructors must not throw. Consume and drop the error.
+ consume_llvm_error(err);
+ }
+ m_ref = nullptr;
+ }
+ if (m_token)
+ m_token->invalidate();
+ }
+
+ void check_valid() const {
+ if (!m_ref)
+ throw LLVMMemoryError("JIT has been disposed");
+ if (!m_token || !m_token->is_valid())
+ throw LLVMMemoryError("JIT has been disposed");
+ }
+
+ static LLVMJITWrapper *host() {
+ if (LLVMInitializeNativeTarget() != 0)
+ throw LLVMError("Failed to initialize native target for JIT");
+ if (LLVMInitializeNativeAsmPrinter() != 0)
+ throw LLVMError("Failed to initialize native ASM printer for JIT");
+
+ LLVMOrcLLJITRef jit = nullptr;
+ LLVMErrorRef err = LLVMOrcCreateLLJIT(&jit, nullptr);
+ if (err) {
+ throw LLVMError("Failed to create host JIT: " + consume_llvm_error(err));
+ }
+ if (!jit)
+ throw LLVMError("Failed to create host JIT");
+ return new LLVMJITWrapper(jit);
+ }
+
+ LLVMJITWrapper &enter() {
+ check_valid();
+ return *this;
+ }
+
+ void exit(const nb::object &, const nb::object &, const nb::object &) {
+ dispose();
+ }
+
+ void dispose() {
+ check_valid();
+ m_pinned_symbols.clear();
+ LLVMErrorRef err = LLVMOrcDisposeLLJIT(m_ref);
+ m_ref = nullptr;
+ if (m_token)
+ m_token->invalidate();
+ if (err) {
+ throw LLVMError("Failed to dispose JIT: " + consume_llvm_error(err));
+ }
+ }
+
+ std::string triple() const {
+ check_valid();
+ const char *triple = LLVMOrcLLJITGetTripleString(m_ref);
+ return triple ? std::string(triple) : std::string();
+ }
+
+ std::string data_layout() const {
+ check_valid();
+ const char *dl = LLVMOrcLLJITGetDataLayoutStr(m_ref);
+ return dl ? std::string(dl) : std::string();
+ }
+
+ void add_module(LLVMModuleWrapper &mod) {
+ check_valid();
+ mod.check_valid();
+ if (mod.m_borrowed) {
+ throw LLVMAssertionError("JIT.add_module requires an owning Module");
+ }
+
+ std::string module_name = mod.get_name();
+ LLVMMemoryBufferRef bitcode = LLVMWriteBitcodeToMemoryBuffer(mod.m_ref);
+ if (!bitcode)
+ throw LLVMError("Failed to serialize module for JIT");
+
+ LLVMContextRef jit_ctx = LLVMContextCreate();
+ LLVMModuleRef jit_mod = nullptr;
+ LLVMBool failed = LLVMParseBitcodeInContext2(jit_ctx, bitcode, &jit_mod);
+ LLVMDisposeMemoryBuffer(bitcode);
+ if (failed) {
+ LLVMContextDispose(jit_ctx);
+ throw LLVMError("Failed to parse serialized module for JIT: " +
+ module_name);
+ }
+
+ const char *jit_triple = LLVMOrcLLJITGetTripleString(m_ref);
+ if (jit_triple && std::string(LLVMGetTarget(jit_mod)).empty())
+ LLVMSetTarget(jit_mod, jit_triple);
+ const char *jit_dl = LLVMOrcLLJITGetDataLayoutStr(m_ref);
+ if (jit_dl && std::string(LLVMGetDataLayoutStr(jit_mod)).empty())
+ LLVMSetDataLayout(jit_mod, jit_dl);
+
+ LLVMOrcThreadSafeContextRef tsc =
+ LLVMOrcCreateNewThreadSafeContextFromLLVMContext(jit_ctx);
+ if (!tsc) {
+ LLVMDisposeModule(jit_mod);
+ // jit_ctx ownership was not transferred if creation failed.
+ LLVMContextDispose(jit_ctx);
+ throw LLVMError("Failed to create ORC thread-safe context for JIT");
+ }
+
+ LLVMOrcThreadSafeModuleRef tsm =
+ LLVMOrcCreateNewThreadSafeModule(jit_mod, tsc);
+ if (!tsm) {
+ LLVMDisposeModule(jit_mod);
+ LLVMOrcDisposeThreadSafeContext(tsc);
+ throw LLVMError("Failed to create ORC thread-safe module for JIT");
+ }
+
+ LLVMErrorRef err = LLVMOrcLLJITAddLLVMIRModule(
+ m_ref, LLVMOrcLLJITGetMainJITDylib(m_ref), tsm);
+ // The ORC C API documents ThreadSafeContext ownership as shared. The
+ // ThreadSafeModule/JIT retains the underlying context data; disposing this
+ // local reference after creating the ThreadSafeModule is required.
+ LLVMOrcDisposeThreadSafeContext(tsc);
+ if (err) {
+ throw LLVMError("Failed to add module to JIT: " +
+ consume_llvm_error(err));
+ }
+
+ mod.dispose();
+ }
+
+ uint64_t lookup(const std::string &name) const {
+ check_valid();
+ if (name.empty())
+ throw LLVMAssertionError("JIT.lookup requires a non-empty symbol name");
+ LLVMOrcExecutorAddress address = 0;
+ LLVMErrorRef err = LLVMOrcLLJITLookup(m_ref, &address, name.c_str());
+ if (err) {
+ throw LLVMError("Failed to look up JIT symbol '" + name + "': " +
+ consume_llvm_error(err));
+ }
+ return static_cast(address);
+ }
+
+ LLVMCtypesFunctionWrapper *ctypes_function(const std::string &name,
+ nb::object restype,
+ nb::object argtypes) const {
+ uint64_t address = lookup(name);
+
+ nb::object ctypes = nb::module_::import_("ctypes");
+ nb::object cfunctype = ctypes.attr("CFUNCTYPE");
+
+ PyObject *argtypes_tuple = PySequence_Tuple(argtypes.ptr());
+ if (!argtypes_tuple)
+ throw nb::python_error();
+
+ Py_ssize_t arg_count = PyTuple_Size(argtypes_tuple);
+ if (arg_count < 0) {
+ Py_DECREF(argtypes_tuple);
+ throw nb::python_error();
+ }
+ PyObject *signature_args = PyTuple_New(arg_count + 1);
+ if (!signature_args) {
+ Py_DECREF(argtypes_tuple);
+ throw nb::python_error();
+ }
+
+ Py_INCREF(restype.ptr());
+ PyTuple_SetItem(signature_args, 0, restype.ptr());
+ for (Py_ssize_t i = 0; i < arg_count; ++i) {
+ PyObject *item = PyTuple_GetItem(argtypes_tuple, i);
+ if (!item) {
+ Py_DECREF(argtypes_tuple);
+ Py_DECREF(signature_args);
+ throw nb::python_error();
+ }
+ Py_INCREF(item);
+ PyTuple_SetItem(signature_args, i + 1, item);
+ }
+ Py_DECREF(argtypes_tuple);
+
+ PyObject *callable_type = PyObject_CallObject(cfunctype.ptr(), signature_args);
+ Py_DECREF(signature_args);
+ if (!callable_type)
+ throw nb::python_error();
+
+ PyObject *address_obj = PyLong_FromUnsignedLongLong(address);
+ if (!address_obj) {
+ Py_DECREF(callable_type);
+ throw nb::python_error();
+ }
+ PyObject *call_args = PyTuple_Pack(1, address_obj);
+ Py_DECREF(address_obj);
+ if (!call_args) {
+ Py_DECREF(callable_type);
+ throw nb::python_error();
+ }
+
+ PyObject *callable = PyObject_CallObject(callable_type, call_args);
+ Py_DECREF(callable_type);
+ Py_DECREF(call_args);
+ if (!callable)
+ throw nb::python_error();
+
+ return new LLVMCtypesFunctionWrapper(nb::steal(callable),
+ m_token);
+ }
+
+ void add_symbol(const std::string &name, nb::object target) {
+ check_valid();
+ if (name.empty())
+ throw LLVMAssertionError("JIT.add_symbol requires a non-empty symbol name");
+
+ bool should_pin = !PyLong_Check(target.ptr());
+ uint64_t address = python_address_from_object(target);
+ if (address == 0)
+ throw LLVMAssertionError("JIT.add_symbol requires a non-null address");
+
+ LLVMOrcSymbolStringPoolEntryRef symbol =
+ LLVMOrcLLJITMangleAndIntern(m_ref, name.c_str());
+ if (!symbol)
+ throw LLVMError("Failed to intern JIT symbol '" + name + "'");
+ LLVMOrcCSymbolMapPair pair;
+ pair.Name = symbol;
+ pair.Sym.Address = static_cast(address);
+ pair.Sym.Flags.GenericFlags = LLVMJITSymbolGenericFlagsExported |
+ LLVMJITSymbolGenericFlagsCallable;
+ pair.Sym.Flags.TargetFlags = 0;
+
+ LLVMOrcMaterializationUnitRef mu = LLVMOrcAbsoluteSymbols(&pair, 1);
+ if (!mu) {
+ LLVMOrcReleaseSymbolStringPoolEntry(symbol);
+ throw LLVMError("Failed to create absolute symbol for JIT symbol '" +
+ name + "'");
+ }
+
+ LLVMErrorRef err = LLVMOrcJITDylibDefine(LLVMOrcLLJITGetMainJITDylib(m_ref),
+ mu);
+ if (err) {
+ LLVMOrcDisposeMaterializationUnit(mu);
+ throw LLVMError("Failed to add JIT symbol '" + name + "': " +
+ consume_llvm_error(err));
+ }
+
+ if (should_pin)
+ m_pinned_symbols.push_back(target);
+ }
+};
+
// =============================================================================
// Memory Buffer Wrapper
// =============================================================================
@@ -9100,7 +9986,7 @@ struct LLVMBinaryManager : NoMoveCopy {
throw LLVMMemoryError("Binary has already been disposed");
if (!m_entered)
throw LLVMMemoryError("Binary manager was not entered");
- m_binary.reset();
+ m_binary->dispose_internal();
m_disposed = true;
}
@@ -9191,15 +10077,22 @@ struct LLVMMetadataWrapper;
struct LLVMDIBuilderWrapper : NoMoveCopy {
LLVMDIBuilderRef m_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
+ std::shared_ptr m_context_token;
std::shared_ptr m_module_token;
LLVMDIBuilderWrapper(LLVMModuleRef mod,
+ std::shared_ptr context_token,
std::shared_ptr module_token)
- : m_module_token(std::move(module_token)) {
+ : m_ctx_ref(mod ? LLVMGetModuleContext(mod) : nullptr),
+ m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {
m_ref = LLVMCreateDIBuilder(mod);
}
- ~LLVMDIBuilderWrapper() {
+ ~LLVMDIBuilderWrapper() { dispose(); }
+
+ void dispose() {
if (m_ref) {
LLVMDisposeDIBuilder(m_ref);
m_ref = nullptr;
@@ -9209,6 +10102,8 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
void check_valid() const {
if (!m_ref)
throw LLVMMemoryError("DIBuilder is null");
+ if (!m_context_token || !m_context_token->is_valid())
+ throw LLVMMemoryError("DIBuilder used after context was destroyed");
if (!m_module_token || !m_module_token->is_valid())
throw LLVMMemoryError("DIBuilder used after module was destroyed");
}
@@ -9225,6 +10120,9 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
LLVMMetadataWrapper create_file(const std::string &filename,
const std::string &directory);
+ LLVMMetadataWrapper file(const std::string &filename,
+ const std::string &directory = ".");
+
LLVMMetadataWrapper create_compile_unit(
int lang, const LLVMMetadataWrapper &file, const std::string &producer,
bool is_optimized, const std::string &flags, unsigned runtime_ver,
@@ -9232,6 +10130,16 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
bool split_debug_inlining, bool debug_info_for_profiling,
const std::string &sys_root, const std::string &sdk);
+ LLVMMetadataWrapper compile_unit(
+ LLVMDWARFSourceLanguage language, const LLVMMetadataWrapper &file,
+ const std::string &producer, bool is_optimized = false,
+ const std::string &flags = "", unsigned runtime_ver = 0,
+ const std::string &split_name = "",
+ LLVMDWARFEmissionKind kind = LLVMDWARFEmissionFull,
+ unsigned dwo_id = 0, bool split_debug_inlining = true,
+ bool debug_info_for_profiling = false, const std::string &sys_root = "",
+ const std::string &sdk = "");
+
LLVMMetadataWrapper create_module(const LLVMMetadataWrapper &parent_scope,
const std::string &name,
const std::string &config_macros,
@@ -9263,6 +10171,16 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
const Iterable ¶m_types,
unsigned flags);
+ LLVMMetadataWrapper function(
+ LLVMFunctionWrapper &fn, const std::string &name,
+ const LLVMMetadataWrapper &file, unsigned line,
+ const LLVMTypeWrapper &return_type,
+ const Iterable ¶m_types,
+ const LLVMMetadataWrapper *scope = nullptr,
+ const std::string &linkage_name = "", bool is_local_to_unit = false,
+ bool is_definition = true, unsigned scope_line = 0, unsigned flags = 0,
+ bool is_optimized = false);
+
// =========================================================================
// Type Creation Methods
// =========================================================================
@@ -9360,6 +10278,15 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
bool always_preserve, unsigned flags,
uint32_t align_in_bits);
+ LLVMMetadataWrapper local_variable(const LLVMMetadataWrapper &scope,
+ const std::string &name,
+ const LLVMMetadataWrapper &file,
+ unsigned line_no,
+ const LLVMTypeWrapper &type,
+ bool always_preserve = true,
+ unsigned flags = 0,
+ uint32_t align_in_bits = 0);
+
LLVMMetadataWrapper create_global_variable_expression(
const LLVMMetadataWrapper &scope, const std::string &name,
const std::string &linkage, const LLVMMetadataWrapper &file,
@@ -9586,24 +10513,104 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
struct LLVMMetadataWrapper {
LLVMMetadataRef m_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
LLVMMetadataWrapper() = default;
- LLVMMetadataWrapper(LLVMMetadataRef ref, std::shared_ptr token)
- : m_ref(ref), m_context_token(std::move(token)) {}
+ LLVMMetadataWrapper(
+ LLVMMetadataRef ref, std::shared_ptr context_token,
+ LLVMContextRef ctx = nullptr,
+ std::shared_ptr module_token = nullptr)
+ : m_ref(ref), m_ctx_ref(ctx), m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
void check_valid() const {
if (!m_ref)
throw LLVMMemoryError("Metadata is null");
if (!m_context_token || !m_context_token->is_valid())
throw LLVMMemoryError("Metadata used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("Metadata used after module was disposed");
+ }
+
+ unsigned kind() const {
+ check_valid();
+ return LLVMGetMetadataKind(m_ref);
+ }
+
+ bool is_string() const { return kind() == LLVMMDStringMetadataKind; }
+
+ bool is_value() const {
+ unsigned k = kind();
+ return k == LLVMConstantAsMetadataMetadataKind ||
+ k == LLVMLocalAsMetadataMetadataKind;
+ }
+
+ bool is_node() const {
+ check_valid();
+ if (!m_ctx_ref)
+ return false;
+ LLVMValueRef value = LLVMMetadataAsValue(m_ctx_ref, m_ref);
+ return LLVMIsAMDNode(value) != nullptr;
+ }
+
+ std::string string() const {
+ check_valid();
+ if (!is_string())
+ throw LLVMAssertionError("metadata is not an MDString");
+ if (!m_ctx_ref)
+ throw LLVMAssertionError("metadata string access requires context");
+ LLVMValueRef value = LLVMMetadataAsValue(m_ctx_ref, m_ref);
+ unsigned len = 0;
+ const char *str = LLVMGetMDString(value, &len);
+ if (!str)
+ throw LLVMAssertionError("metadata is not an MDString");
+ return std::string(str, len);
+ }
+
+ LLVMValueWrapper value() const {
+ check_valid();
+ if (!is_value())
+ throw LLVMAssertionError(
+ "metadata is not ConstantAsMetadata or LocalAsMetadata");
+ PyErr_SetString(PyExc_NotImplementedError,
+ "LLVM-C cannot unwrap ValueAsMetadata to Value");
+ throw nb::python_error();
+ }
+
+ LLVMValueRef as_md_node_value() const {
+ check_valid();
+ if (!m_ctx_ref)
+ throw LLVMAssertionError("metadata node access requires context");
+ LLVMValueRef value = LLVMMetadataAsValue(m_ctx_ref, m_ref);
+ if (!LLVMIsAMDNode(value))
+ throw LLVMAssertionError("metadata is not an MDNode");
+ return value;
+ }
+
+ std::vector operands() const {
+ LLVMValueRef value = as_md_node_value();
+ unsigned count = LLVMGetMDNodeNumOperands(value);
+ std::vector refs(count);
+ LLVMGetMDNodeOperands(value, refs.data());
+ std::vector result;
+ result.reserve(count);
+ for (LLVMValueRef operand : refs) {
+ if (!operand)
+ throw LLVMAssertionError("metadata node operand is null");
+ result.emplace_back(LLVMValueAsMetadata(operand), m_context_token,
+ m_ctx_ref, m_module_token);
+ }
+ return result;
}
// Convert metadata to value (requires context)
LLVMValueWrapper as_value(LLVMContextWrapper &ctx) const {
check_valid();
ctx.check_valid();
- return LLVMValueWrapper(LLVMMetadataAsValue(ctx.m_ref, m_ref), ctx.m_token);
+ return LLVMValueWrapper(LLVMMetadataAsValue(ctx.m_ref, m_ref), ctx.m_token,
+ m_module_token);
}
// Replace all uses of this temporary metadata with another
@@ -9620,6 +10627,462 @@ struct LLVMMetadataWrapper {
}
};
+static LLVMModuleRef module_for_metadata_value(LLVMValueRef value) {
+ return module_for_value_if_parented(value);
+}
+
+static LLVMContextRef context_for_metadata_value(LLVMValueRef value) {
+ if (!LLVMIsAInstruction(value) && !LLVMIsAGlobalValue(value)) {
+ throw LLVMAssertionError(
+ "metadata view requires an instruction or global value");
+ }
+
+ LLVMTypeRef ty = LLVMTypeOf(value);
+ if (ty)
+ return LLVMGetTypeContext(ty);
+
+ if (LLVMIsAInstruction(value)) {
+ LLVMBasicBlockRef bb = LLVMGetInstructionParent(value);
+ if (!bb)
+ throw LLVMAssertionError("metadata requires an instruction in a basic block");
+ LLVMValueRef fn = LLVMGetBasicBlockParent(bb);
+ if (!fn)
+ throw LLVMAssertionError("metadata requires an instruction in a function");
+ LLVMModuleRef mod = LLVMGetGlobalParent(fn);
+ if (!mod)
+ throw LLVMAssertionError("metadata requires an instruction in a module");
+ return LLVMGetModuleContext(mod);
+ }
+
+ LLVMModuleRef mod = LLVMGetGlobalParent(value);
+ if (!mod)
+ throw LLVMAssertionError("metadata requires a global value in a module");
+ return LLVMGetModuleContext(mod);
+}
+
+static void require_metadata_context(LLVMContextRef ctx,
+ const LLVMMetadataWrapper &md,
+ const char *api_name) {
+ if (md.m_ctx_ref && md.m_ctx_ref != ctx) {
+ throw LLVMAssertionError(std::string(api_name) +
+ " requires metadata from the same context");
+ }
+}
+
+static LLVMMetadataRef normalize_metadata_for_attachment(
+ LLVMContextRef ctx, const LLVMMetadataWrapper &md) {
+ md.check_valid();
+ require_metadata_context(ctx, md, "metadata attachment");
+ LLVMMetadataRef metadata_ref = md.m_ref;
+ LLVMValueRef md_value = LLVMMetadataAsValue(ctx, metadata_ref);
+ bool is_md_node = LLVMIsAMDNode(md_value) && !LLVMIsAValueAsMetadata(md_value);
+ if (!is_md_node) {
+ LLVMMetadataRef refs[] = {metadata_ref};
+ metadata_ref = LLVMMDNodeInContext2(ctx, refs, 1);
+ }
+ return metadata_ref;
+}
+
+struct LLVMMetadataMapWrapper {
+ LLVMValueRef m_value_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
+ std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
+
+ LLVMMetadataMapWrapper() = default;
+ LLVMMetadataMapWrapper(LLVMValueRef value, LLVMContextRef ctx,
+ std::shared_ptr context_token,
+ std::shared_ptr module_token)
+ : m_value_ref(value), m_ctx_ref(ctx),
+ m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
+
+ void check_valid() const {
+ if (!m_value_ref)
+ throw LLVMMemoryError("Metadata view has no value");
+ if (!m_ctx_ref)
+ throw LLVMMemoryError("Metadata view has no context");
+ if (!m_context_token || !m_context_token->is_valid())
+ throw LLVMMemoryError("Metadata view used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError("Metadata view used after module was disposed");
+ if (!LLVMIsAInstruction(m_value_ref) && !LLVMIsAGlobalValue(m_value_ref)) {
+ throw LLVMAssertionError(
+ "metadata view requires an instruction or global value");
+ }
+ }
+
+ unsigned kind_id(const std::string &name) const {
+ check_valid();
+ if (name.empty())
+ throw LLVMAssertionError("metadata kind name cannot be empty");
+ return LLVMGetMDKindIDInContext(m_ctx_ref, name.c_str(), name.size());
+ }
+
+ std::optional get(const std::string &name) const {
+ unsigned kind = kind_id(name);
+ if (LLVMIsAInstruction(m_value_ref)) {
+ LLVMValueRef val = LLVMGetMetadata(m_value_ref, kind);
+ if (!val)
+ return std::nullopt;
+ return LLVMMetadataWrapper(LLVMValueAsMetadata(val), m_context_token,
+ m_ctx_ref, m_module_token);
+ }
+
+ size_t count = 0;
+ LLVMValueMetadataEntry *entries = LLVMGlobalCopyAllMetadata(m_value_ref, &count);
+ for (size_t i = 0; i < count; ++i) {
+ if (LLVMValueMetadataEntriesGetKind(entries, i) == kind) {
+ LLVMMetadataRef md = LLVMValueMetadataEntriesGetMetadata(entries, i);
+ LLVMDisposeValueMetadataEntries(entries);
+ return LLVMMetadataWrapper(md, m_context_token, m_ctx_ref,
+ m_module_token);
+ }
+ }
+ if (entries)
+ LLVMDisposeValueMetadataEntries(entries);
+ return std::nullopt;
+ }
+
+ LLVMMetadataWrapper get_item(const std::string &name) const {
+ auto md = get(name);
+ if (!md)
+ throw nb::key_error(name.c_str());
+ return *md;
+ }
+
+ bool contains(const std::string &name) const { return get(name).has_value(); }
+
+ void set(const std::string &name, const LLVMMetadataWrapper &md) {
+ unsigned kind = kind_id(name);
+ LLVMMetadataRef metadata_ref = normalize_metadata_for_attachment(m_ctx_ref, md);
+ if (LLVMIsAGlobalValue(m_value_ref)) {
+ LLVMGlobalSetMetadata(m_value_ref, kind, metadata_ref);
+ } else {
+ LLVMValueRef md_value = LLVMMetadataAsValue(m_ctx_ref, metadata_ref);
+ LLVMSetMetadata(m_value_ref, kind, md_value);
+ }
+ }
+
+ void erase(const std::string &name) {
+ unsigned kind = kind_id(name);
+ if (LLVMIsAGlobalValue(m_value_ref)) {
+ LLVMGlobalEraseMetadata(m_value_ref, kind);
+ } else {
+ LLVMSetMetadata(m_value_ref, kind, nullptr);
+ }
+ }
+
+ void attach_entry(LLVMValueRef target_ref, LLVMContextRef target_ctx,
+ unsigned kind, LLVMMetadataRef md) const {
+ LLVMMetadataWrapper md_wrapper(md, m_context_token, m_ctx_ref,
+ m_module_token);
+ LLVMMetadataRef metadata_ref =
+ normalize_metadata_for_attachment(target_ctx, md_wrapper);
+ if (LLVMIsAGlobalValue(target_ref)) {
+ LLVMGlobalSetMetadata(target_ref, kind, metadata_ref);
+ } else {
+ LLVMValueRef md_value = LLVMMetadataAsValue(target_ctx, metadata_ref);
+ LLVMSetMetadata(target_ref, kind, md_value);
+ }
+ }
+
+ void copy_entries_to(LLVMValueMetadataEntry *entries, size_t count,
+ LLVMValueRef target_ref,
+ LLVMContextRef target_ctx) const {
+ for (size_t i = 0; i < count; ++i) {
+ unsigned kind = LLVMValueMetadataEntriesGetKind(entries, i);
+ LLVMMetadataRef md = LLVMValueMetadataEntriesGetMetadata(entries, i);
+ attach_entry(target_ref, target_ctx, kind, md);
+ }
+ }
+
+ void copy_to(const LLVMValueWrapper &target,
+ bool include_debug_location = false) const {
+ check_valid();
+ target.check_valid();
+ if (!LLVMIsAInstruction(target.m_ref) && !LLVMIsAGlobalValue(target.m_ref)) {
+ throw LLVMAssertionError(
+ "metadata copy target requires an instruction or global value");
+ }
+ if (target.m_context_token != m_context_token) {
+ throw LLVMAssertionError(
+ "metadata copy requires source and target in the same context");
+ }
+ if (include_debug_location &&
+ (!LLVMIsAInstruction(m_value_ref) || !LLVMIsAInstruction(target.m_ref))) {
+ throw LLVMAssertionError(
+ "include_debug_location requires instruction source and target");
+ }
+
+ LLVMContextRef target_ctx = context_for_metadata_value(target.m_ref);
+ size_t count = 0;
+ LLVMValueMetadataEntry *entries = nullptr;
+ if (LLVMIsAInstruction(m_value_ref)) {
+ entries = LLVMInstructionGetAllMetadataOtherThanDebugLoc(m_value_ref,
+ &count);
+ } else {
+ entries = LLVMGlobalCopyAllMetadata(m_value_ref, &count);
+ }
+
+ copy_entries_to(entries, count, target.m_ref, target_ctx);
+ if (entries)
+ LLVMDisposeValueMetadataEntries(entries);
+
+ if (include_debug_location) {
+ LLVMInstructionSetDebugLoc(target.m_ref,
+ LLVMInstructionGetDebugLoc(m_value_ref));
+ }
+ }
+};
+
+struct LLVMNamedMetadataListWrapper {
+ LLVMModuleRef m_module_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
+ std::string m_name;
+ std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
+
+ LLVMNamedMetadataListWrapper() = default;
+ LLVMNamedMetadataListWrapper(LLVMModuleRef mod, LLVMContextRef ctx,
+ std::string name,
+ std::shared_ptr context_token,
+ std::shared_ptr module_token)
+ : m_module_ref(mod), m_ctx_ref(ctx), m_name(std::move(name)),
+ m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
+
+ void check_valid() const {
+ if (!m_module_ref)
+ throw LLVMMemoryError("Named metadata view has no module");
+ if (!m_context_token || !m_context_token->is_valid())
+ throw LLVMMemoryError("Named metadata view used after context was destroyed");
+ if (!m_module_token || !m_module_token->is_valid())
+ throw LLVMMemoryError("Named metadata view used after module was disposed");
+ }
+
+ size_t size() const {
+ check_valid();
+ return LLVMGetNamedMetadataNumOperands(m_module_ref, m_name.c_str());
+ }
+
+ void append(const LLVMMetadataWrapper &md) {
+ check_valid();
+ md.check_valid();
+ if (md.m_ctx_ref && md.m_ctx_ref != m_ctx_ref) {
+ throw LLVMAssertionError(
+ "named metadata requires metadata from the same context");
+ }
+ LLVMValueRef val = LLVMMetadataAsValue(m_ctx_ref, md.m_ref);
+ LLVMAddNamedMetadataOperand(m_module_ref, m_name.c_str(), val);
+ }
+
+ std::vector all() const {
+ check_valid();
+ unsigned count = LLVMGetNamedMetadataNumOperands(m_module_ref, m_name.c_str());
+ if (count == 0)
+ return {};
+ std::vector values(count);
+ LLVMGetNamedMetadataOperands(m_module_ref, m_name.c_str(), values.data());
+ std::vector result;
+ result.reserve(count);
+ for (LLVMValueRef value : values) {
+ result.emplace_back(LLVMValueAsMetadata(value), m_context_token,
+ m_ctx_ref, m_module_token);
+ }
+ return result;
+ }
+
+ LLVMMetadataWrapper get_item(size_t index) const {
+ auto values = all();
+ if (index >= values.size())
+ throw nb::index_error("named metadata index out of range");
+ return values[index];
+ }
+};
+
+struct LLVMNamedMetadataMapWrapper {
+ LLVMModuleRef m_module_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
+ std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
+
+ LLVMNamedMetadataMapWrapper() = default;
+ LLVMNamedMetadataMapWrapper(LLVMModuleRef mod, LLVMContextRef ctx,
+ std::shared_ptr context_token,
+ std::shared_ptr module_token)
+ : m_module_ref(mod), m_ctx_ref(ctx),
+ m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
+
+ void check_valid() const {
+ if (!m_module_ref)
+ throw LLVMMemoryError("Named metadata map has no module");
+ if (!m_context_token || !m_context_token->is_valid())
+ throw LLVMMemoryError("Named metadata map used after context was destroyed");
+ if (!m_module_token || !m_module_token->is_valid())
+ throw LLVMMemoryError("Named metadata map used after module was disposed");
+ }
+
+ bool contains(const std::string &name) const {
+ check_valid();
+ return LLVMGetNamedMetadata(m_module_ref, name.c_str(), name.size()) != nullptr;
+ }
+
+ LLVMNamedMetadataListWrapper get_item(const std::string &name) const {
+ check_valid();
+ if (name.empty())
+ throw LLVMAssertionError("named metadata name cannot be empty");
+ LLVMGetOrInsertNamedMetadata(m_module_ref, name.c_str(), name.size());
+ return LLVMNamedMetadataListWrapper(m_module_ref, m_ctx_ref, name,
+ m_context_token, m_module_token);
+ }
+
+ std::optional get(const std::string &name) const {
+ check_valid();
+ if (!contains(name))
+ return std::nullopt;
+ return LLVMNamedMetadataListWrapper(m_module_ref, m_ctx_ref, name,
+ m_context_token, m_module_token);
+ }
+
+ std::vector keys() const {
+ check_valid();
+ std::vector result;
+ for (LLVMNamedMDNodeRef cur = LLVMGetFirstNamedMetadata(m_module_ref); cur;
+ cur = LLVMGetNextNamedMetadata(cur)) {
+ size_t len = 0;
+ const char *name = LLVMGetNamedMetadataName(cur, &len);
+ result.emplace_back(name, len);
+ }
+ return result;
+ }
+};
+
+struct LLVMModuleFlagsWrapper {
+ LLVMModuleRef m_module_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
+ std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
+
+ LLVMModuleFlagsWrapper() = default;
+ LLVMModuleFlagsWrapper(LLVMModuleRef mod,
+ std::shared_ptr context_token,
+ std::shared_ptr module_token)
+ : m_module_ref(mod), m_ctx_ref(mod ? LLVMGetModuleContext(mod) : nullptr),
+ m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)) {}
+
+ void check_valid() const {
+ if (!m_module_ref)
+ throw LLVMMemoryError("Module flags view has no module");
+ if (!m_context_token || !m_context_token->is_valid())
+ throw LLVMMemoryError("Module flags view used after context was destroyed");
+ if (!m_module_token || !m_module_token->is_valid())
+ throw LLVMMemoryError("Module flags view used after module was disposed");
+ }
+
+ void add(const std::string &key, LLVMModuleFlagBehavior behavior,
+ const LLVMMetadataWrapper &value) {
+ check_valid();
+ value.check_valid();
+ if (value.m_ctx_ref && value.m_ctx_ref != m_ctx_ref) {
+ throw LLVMAssertionError(
+ "module flag requires metadata from the same context");
+ }
+ if (key.empty())
+ throw LLVMAssertionError("module flag key cannot be empty");
+ LLVMAddModuleFlag(m_module_ref, behavior, key.c_str(), key.size(), value.m_ref);
+ }
+
+ std::optional get(const std::string &key) const {
+ check_valid();
+ LLVMMetadataRef md = LLVMGetModuleFlag(m_module_ref, key.c_str(), key.size());
+ if (!md)
+ return std::nullopt;
+ return LLVMMetadataWrapper(md, m_context_token, m_ctx_ref,
+ m_module_token);
+ }
+
+ LLVMMetadataWrapper get_item(const std::string &key) const {
+ auto md = get(key);
+ if (!md)
+ throw nb::key_error(key.c_str());
+ return *md;
+ }
+
+ bool contains(const std::string &key) const { return get(key).has_value(); }
+
+ std::vector keys() const {
+ check_valid();
+ size_t count = 0;
+ LLVMModuleFlagEntry *entries = LLVMCopyModuleFlagsMetadata(m_module_ref, &count);
+ std::vector result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ size_t len = 0;
+ const char *key = LLVMModuleFlagEntriesGetKey(entries, i, &len);
+ result.emplace_back(key, len);
+ }
+ if (entries)
+ LLVMDisposeModuleFlagsMetadata(entries);
+ return result;
+ }
+};
+
+struct LLVMBuilderDebugLocationManager : NoMoveCopy {
+ LLVMBuilderRef m_builder_ref = nullptr;
+ LLVMMetadataRef m_new_loc = nullptr;
+ LLVMMetadataRef m_saved_loc = nullptr;
+ std::shared_ptr m_context_token;
+ std::shared_ptr m_module_token;
+ std::shared_ptr m_builder_token;
+ bool m_entered = false;
+
+ LLVMBuilderDebugLocationManager() = default;
+ LLVMBuilderDebugLocationManager(
+ LLVMBuilderRef builder, LLVMMetadataRef loc,
+ std::shared_ptr context_token,
+ std::shared_ptr module_token,
+ std::shared_ptr builder_token)
+ : m_builder_ref(builder), m_new_loc(loc),
+ m_context_token(std::move(context_token)),
+ m_module_token(std::move(module_token)),
+ m_builder_token(std::move(builder_token)) {}
+
+ void check_valid() const {
+ if (!m_builder_ref)
+ throw LLVMMemoryError("Debug location manager has no builder");
+ if (!m_builder_token || !m_builder_token->is_valid())
+ throw LLVMMemoryError(
+ "Debug location manager used after builder was disposed");
+ if (!m_context_token || !m_context_token->is_valid())
+ throw LLVMMemoryError(
+ "Debug location manager used after context was destroyed");
+ if (m_module_token && !m_module_token->is_valid())
+ throw LLVMMemoryError(
+ "Debug location manager used after module was disposed");
+ }
+
+ LLVMBuilderDebugLocationManager &enter() {
+ check_valid();
+ if (m_entered)
+ throw LLVMMemoryError("Debug location manager already entered");
+ m_saved_loc = LLVMGetCurrentDebugLocation2(m_builder_ref);
+ LLVMSetCurrentDebugLocation2(m_builder_ref, m_new_loc);
+ m_entered = true;
+ return *this;
+ }
+
+ void exit(const nb::object &, const nb::object &, const nb::object &) {
+ check_valid();
+ if (!m_entered)
+ throw LLVMMemoryError("Debug location manager was not entered");
+ LLVMSetCurrentDebugLocation2(m_builder_ref, m_saved_loc);
+ m_entered = false;
+ }
+};
+
// Implementation of get_metadata for ValueMetadataEntriesWrapper
inline LLVMMetadataWrapper LLVMValueMetadataEntriesWrapper_get_metadata(
const LLVMValueMetadataEntriesWrapper &entries, unsigned index) {
@@ -9634,7 +11097,21 @@ inline LLVMMetadataWrapper LLVMValueMetadataEntriesWrapper_get_metadata(
// Implementation of LLVMValueWrapper::as_metadata() - needs LLVMMetadataWrapper
inline LLVMMetadataWrapper LLVMValueWrapper::as_metadata() const {
check_valid();
- return LLVMMetadataWrapper(LLVMValueAsMetadata(m_ref), m_context_token);
+ LLVMContextRef ctx_ref = nullptr;
+ LLVMTypeRef ty = LLVMTypeOf(m_ref);
+ if (ty)
+ ctx_ref = LLVMGetTypeContext(ty);
+ return LLVMMetadataWrapper(LLVMValueAsMetadata(m_ref), m_context_token,
+ ctx_ref, m_module_token);
+}
+
+inline LLVMMetadataMapWrapper LLVMValueWrapper::metadata() const {
+ check_valid();
+ LLVMContextRef ctx_ref = context_for_metadata_value(m_ref);
+ std::shared_ptr module_token = m_module_token;
+ if (!module_token)
+ module_token = module_token_for_module(module_for_metadata_value(m_ref));
+ return LLVMMetadataMapWrapper(m_ref, ctx_ref, m_context_token, module_token);
}
// Implementation of replace_md_node_operand_with - needs LLVMMetadataWrapper
@@ -9686,7 +11163,7 @@ inline LLVMMetadataWrapper
LLVMContextWrapper::md_string(const std::string &str) {
check_valid();
return LLVMMetadataWrapper(
- LLVMMDStringInContext2(m_ref, str.c_str(), str.size()), m_token);
+ LLVMMDStringInContext2(m_ref, str.c_str(), str.size()), m_token, m_ref);
}
// Implementation of LLVMContextWrapper::md_node() - needs LLVMMetadataWrapper
@@ -9697,10 +11174,11 @@ LLVMContextWrapper::md_node(const Iterable &mds) {
refs.reserve(mds.size());
for (const auto &md : mds) {
md.check_valid();
+ require_metadata_context(m_ref, md, "Context.md_node");
refs.push_back(md.m_ref);
}
return LLVMMetadataWrapper(
- LLVMMDNodeInContext2(m_ref, refs.data(), refs.size()), m_token);
+ LLVMMDNodeInContext2(m_ref, refs.data(), refs.size()), m_token, m_ref);
}
inline LLVMMetadataWrapper LLVMContextWrapper::create_debug_location(
@@ -9708,15 +11186,52 @@ inline LLVMMetadataWrapper LLVMContextWrapper::create_debug_location(
const LLVMMetadataWrapper *inlined_at) {
check_valid();
scope.check_valid();
+ require_metadata_context(m_ref, scope, "Context.debug_location scope");
LLVMMetadataRef inlined = nullptr;
if (inlined_at) {
inlined_at->check_valid();
+ require_metadata_context(m_ref, *inlined_at,
+ "Context.debug_location inlined_at");
inlined = inlined_at->m_ref;
}
return LLVMMetadataWrapper(
LLVMDIBuilderCreateDebugLocation(m_ref, line, column, scope.m_ref,
inlined),
- m_token);
+ m_token, m_ref);
+}
+
+inline LLVMMetadataWrapper LLVMContextWrapper::debug_location(
+ unsigned line, unsigned column, const LLVMMetadataWrapper &scope,
+ const LLVMMetadataWrapper *inlined_at) {
+ return create_debug_location(line, column, scope, inlined_at);
+}
+
+inline LLVMBuilderDebugLocationManager *
+LLVMBuilderWrapper::debug_location(const LLVMMetadataWrapper &loc) const {
+ check_valid();
+ loc.check_valid();
+ require_metadata_context(m_ctx_ref, loc, "Builder.debug_location");
+ return new LLVMBuilderDebugLocationManager(m_ref, loc.m_ref, m_context_token,
+ m_module_token, m_token);
+}
+
+inline LLVMBuilderDebugLocationManager *LLVMBuilderWrapper::debug_location(
+ unsigned line, unsigned column, const LLVMMetadataWrapper &scope,
+ const LLVMMetadataWrapper *inlined_at) const {
+ check_valid();
+ scope.check_valid();
+ require_metadata_context(m_ctx_ref, scope, "Builder.debug_location scope");
+ LLVMMetadataRef inlined = nullptr;
+ if (inlined_at) {
+ inlined_at->check_valid();
+ require_metadata_context(m_ctx_ref, *inlined_at,
+ "Builder.debug_location inlined_at");
+ inlined = inlined_at->m_ref;
+ }
+ LLVMMetadataRef loc = LLVMDIBuilderCreateDebugLocation(
+ m_ctx_ref, line, column, scope.m_ref, inlined);
+ return new LLVMBuilderDebugLocationManager(m_ref, loc, m_context_token,
+ m_module_token, m_token);
}
// Implementation of LLVMContextWrapper::create_type_attribute() - needs
@@ -9739,6 +11254,7 @@ LLVMModuleWrapper::add_named_metadata_operand(const std::string &name,
const LLVMMetadataWrapper &md) {
check_valid();
md.check_valid();
+ require_metadata_context(m_ctx_ref, md, "Module.add_named_metadata_operand");
// Need to convert metadata to value first for LLVMAddNamedMetadataOperand
LLVMValueRef val = LLVMMetadataAsValue(m_ctx_ref, md.m_ref);
LLVMAddNamedMetadataOperand(m_ref, name.c_str(), val);
@@ -9751,6 +11267,7 @@ inline void LLVMModuleWrapper::add_module_flag(LLVMModuleFlagBehavior behavior,
const LLVMMetadataWrapper &val) {
check_valid();
val.check_valid();
+ require_metadata_context(m_ctx_ref, val, "Module.add_module_flag");
LLVMAddModuleFlag(m_ref, behavior, key.c_str(), key.size(), val.m_ref);
}
@@ -9762,7 +11279,17 @@ LLVMModuleWrapper::get_module_flag(const std::string &key) {
LLVMMetadataRef md = LLVMGetModuleFlag(m_ref, key.c_str(), key.size());
if (!md)
return std::nullopt;
- return LLVMMetadataWrapper(md, m_context_token);
+ return LLVMMetadataWrapper(md, m_context_token, m_ctx_ref, m_token);
+}
+
+inline LLVMNamedMetadataMapWrapper LLVMModuleWrapper::named_metadata() const {
+ check_valid();
+ return LLVMNamedMetadataMapWrapper(m_ref, m_ctx_ref, m_context_token, m_token);
+}
+
+inline LLVMModuleFlagsWrapper LLVMModuleWrapper::module_flags() const {
+ check_valid();
+ return LLVMModuleFlagsWrapper(m_ref, m_context_token, m_token);
}
// Implementation of LLVMFunctionWrapper::set_subprogram() - needs
@@ -9777,6 +11304,31 @@ inline void LLVMFunctionWrapper::set_subprogram(const LLVMMetadataWrapper &sp) {
// DIBuilder Method Implementations
// =============================================================================
+static LLVMMetadataRef create_debug_type_from_ir_type(LLVMDIBuilderRef dib,
+ LLVMTypeRef ty) {
+ LLVMTypeKind kind = LLVMGetTypeKind(ty);
+ switch (kind) {
+ case LLVMVoidTypeKind:
+ return nullptr;
+ case LLVMIntegerTypeKind: {
+ unsigned width = LLVMGetIntTypeWidth(ty);
+ std::string name = width == 1 ? "bool" : "i" + std::to_string(width);
+ unsigned encoding = width == 1 ? 0x02 : 0x05; // DW_ATE_boolean/signed
+ return LLVMDIBuilderCreateBasicType(dib, name.c_str(), name.size(), width,
+ encoding, LLVMDIFlagZero);
+ }
+ case LLVMFloatTypeKind:
+ return LLVMDIBuilderCreateBasicType(dib, "float", 5, 32, 0x04,
+ LLVMDIFlagZero);
+ case LLVMDoubleTypeKind:
+ return LLVMDIBuilderCreateBasicType(dib, "double", 6, 64, 0x04,
+ LLVMDIFlagZero);
+ default:
+ throw LLVMAssertionError(
+ "DIBuilder type recipe supports void, integer, float, and double IR types");
+ }
+}
+
// File & Scope Creation
inline LLVMMetadataWrapper
LLVMDIBuilderWrapper::create_file(const std::string &filename,
@@ -9785,7 +11337,13 @@ LLVMDIBuilderWrapper::create_file(const std::string &filename,
return LLVMMetadataWrapper(
LLVMDIBuilderCreateFile(m_ref, filename.c_str(), filename.size(),
directory.c_str(), directory.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
+}
+
+inline LLVMMetadataWrapper
+LLVMDIBuilderWrapper::file(const std::string &filename,
+ const std::string &directory) {
+ return create_file(filename, directory);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_compile_unit(
@@ -9804,7 +11362,21 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_compile_unit(
(LLVMDWARFEmissionKind)kind, dwo_id, split_debug_inlining,
debug_info_for_profiling, sys_root.c_str(), sys_root.size(),
sdk.c_str(), sdk.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
+}
+
+inline LLVMMetadataWrapper LLVMDIBuilderWrapper::compile_unit(
+ LLVMDWARFSourceLanguage language, const LLVMMetadataWrapper &file,
+ const std::string &producer, bool is_optimized,
+ const std::string &flags, unsigned runtime_ver,
+ const std::string &split_name, LLVMDWARFEmissionKind kind,
+ unsigned dwo_id, bool split_debug_inlining,
+ bool debug_info_for_profiling, const std::string &sys_root,
+ const std::string &sdk) {
+ return create_compile_unit((int)language, file, producer, is_optimized, flags,
+ runtime_ver, split_name, (unsigned)kind, dwo_id,
+ split_debug_inlining, debug_info_for_profiling,
+ sys_root, sdk);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_module(
@@ -9818,7 +11390,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_module(
m_ref, parent_scope.m_ref, name.c_str(), name.size(),
config_macros.c_str(), config_macros.size(), include_path.c_str(),
include_path.size(), api_notes_file.c_str(), api_notes_file.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -9830,7 +11402,7 @@ LLVMDIBuilderWrapper::create_namespace(const LLVMMetadataWrapper &parent_scope,
return LLVMMetadataWrapper(
LLVMDIBuilderCreateNameSpace(m_ref, parent_scope.m_ref, name.c_str(),
name.size(), export_symbols),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -9842,7 +11414,7 @@ LLVMDIBuilderWrapper::create_lexical_block(const LLVMMetadataWrapper &scope,
file.check_valid();
return LLVMMetadataWrapper(LLVMDIBuilderCreateLexicalBlock(
m_ref, scope.m_ref, file.m_ref, line, column),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Function & Subroutine Creation
@@ -9861,7 +11433,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_function(
m_ref, scope.m_ref, name.c_str(), name.size(), linkage_name.c_str(),
linkage_name.size(), file.m_ref, line_no, type, is_local_to_unit,
is_definition, scope_line, (LLVMDIFlags)flags, is_optimized),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_subroutine_type(
@@ -9878,7 +11450,44 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_subroutine_type(
return LLVMMetadataWrapper(
LLVMDIBuilderCreateSubroutineType(m_ref, file.m_ref, param_refs.data(),
param_refs.size(), (LLVMDIFlags)flags),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
+}
+
+inline LLVMMetadataWrapper LLVMDIBuilderWrapper::function(
+ LLVMFunctionWrapper &fn, const std::string &name,
+ const LLVMMetadataWrapper &file, unsigned line,
+ const LLVMTypeWrapper &return_type,
+ const Iterable ¶m_types,
+ const LLVMMetadataWrapper *scope, const std::string &linkage_name,
+ bool is_local_to_unit, bool is_definition, unsigned scope_line,
+ unsigned flags, bool is_optimized) {
+ check_valid();
+ fn.check_valid();
+ file.check_valid();
+ return_type.check_valid();
+ LLVMMetadataRef scope_ref = scope ? scope->m_ref : file.m_ref;
+ if (scope)
+ scope->check_valid();
+
+ std::vector di_types;
+ di_types.reserve(param_types.size() + 1);
+ di_types.push_back(create_debug_type_from_ir_type(m_ref, return_type.m_ref));
+ for (const auto ¶m_ty : param_types) {
+ param_ty.check_valid();
+ di_types.push_back(create_debug_type_from_ir_type(m_ref, param_ty.m_ref));
+ }
+
+ LLVMMetadataRef subroutine_type = LLVMDIBuilderCreateSubroutineType(
+ m_ref, file.m_ref, di_types.data(), di_types.size(), LLVMDIFlagZero);
+ std::string linkage = linkage_name.empty() ? fn.get_name() : linkage_name;
+ unsigned actual_scope_line = scope_line == 0 ? line : scope_line;
+ LLVMMetadataRef sp = LLVMDIBuilderCreateFunction(
+ m_ref, scope_ref, name.c_str(), name.size(), linkage.c_str(), linkage.size(),
+ file.m_ref, line, subroutine_type, is_local_to_unit, is_definition,
+ actual_scope_line, (LLVMDIFlags)flags, is_optimized);
+ LLVMMetadataWrapper wrapper(sp, m_context_token, m_ctx_ref, m_module_token);
+ fn.set_subprogram(wrapper);
+ return wrapper;
}
// Type Creation
@@ -9890,7 +11499,7 @@ LLVMDIBuilderWrapper::create_basic_type(const std::string &name,
return LLVMMetadataWrapper(
LLVMDIBuilderCreateBasicType(m_ref, name.c_str(), name.size(),
size_in_bits, encoding, (LLVMDIFlags)flags),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_pointer_type(
@@ -9902,7 +11511,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_pointer_type(
LLVMDIBuilderCreatePointerType(m_ref, pointee.m_ref, size_in_bits,
align_in_bits, address_space, name.c_str(),
name.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_vector_type(
@@ -9921,7 +11530,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_vector_type(
LLVMDIBuilderCreateVectorType(m_ref, size_in_bits, align_in_bits,
element_type.m_ref, sub_refs.data(),
sub_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_typedef(
@@ -9936,7 +11545,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_typedef(
LLVMDIBuilderCreateTypedef(m_ref, type.m_ref, name.c_str(), name.size(),
file.m_ref, line_no, scope.m_ref,
align_in_bits),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_struct_type(
@@ -9964,7 +11573,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_struct_type(
elem_refs.empty() ? nullptr : elem_refs.data(),
static_cast(elem_refs.size()), runtime_lang, vtable,
unique_id.c_str(), unique_id.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_enumeration_type(
@@ -9988,7 +11597,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_enumeration_type(
file.m_ref, line_number, size_in_bits,
align_in_bits, elem_refs.data(),
elem_refs.size(), underlying_type.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_forward_decl(
@@ -10004,7 +11613,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_forward_decl(
scope.m_ref, file.m_ref, line,
runtime_lang, size_in_bits, align_in_bits,
unique_id.c_str(), unique_id.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10021,7 +11630,7 @@ LLVMDIBuilderWrapper::create_replaceable_composite_type(
m_ref, tag, name.c_str(), name.size(), scope.m_ref, file.m_ref, line,
runtime_lang, size_in_bits, align_in_bits, (LLVMDIFlags)flags,
unique_id.c_str(), unique_id.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_subrange_type(
@@ -10045,7 +11654,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_subrange_type(
line, file.m_ref, size_in_bits, align_in_bits,
(LLVMDIFlags)flags, element_type.m_ref, lb, ub,
st, bi),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_set_type(
@@ -10060,7 +11669,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_set_type(
LLVMDIBuilderCreateSetType(m_ref, scope.m_ref, name.c_str(), name.size(),
file.m_ref, line, size_in_bits, align_in_bits,
base_type.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_dynamic_array_type(
@@ -10091,7 +11700,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_dynamic_array_type(
m_ref, scope.m_ref, name.c_str(), name.size(), line, file.m_ref,
size_in_bits, align_in_bits, element_type.m_ref, sub_refs.data(),
sub_refs.size(), data_location.m_ref, assoc, alloc, rnk, stride_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Variable & Expression
@@ -10107,7 +11716,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_parameter_variable(
m_ref, scope.m_ref, name.c_str(), name.size(),
arg_no, file.m_ref, line_no, type.m_ref,
always_preserve, (LLVMDIFlags)flags),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_auto_variable(
@@ -10123,7 +11732,26 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_auto_variable(
LLVMDIBuilderCreateAutoVariable(
m_ref, scope.m_ref, name.c_str(), name.size(), file.m_ref, line_no,
type.m_ref, always_preserve, (LLVMDIFlags)flags, align_in_bits),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
+}
+
+inline LLVMMetadataWrapper LLVMDIBuilderWrapper::local_variable(
+ const LLVMMetadataWrapper &scope, const std::string &name,
+ const LLVMMetadataWrapper &file, unsigned line_no,
+ const LLVMTypeWrapper &type, bool always_preserve, unsigned flags,
+ uint32_t align_in_bits) {
+ check_valid();
+ scope.check_valid();
+ file.check_valid();
+ type.check_valid();
+ LLVMMetadataRef di_type = create_debug_type_from_ir_type(m_ref, type.m_ref);
+ if (!di_type)
+ throw LLVMAssertionError("local_variable type cannot be void");
+ return LLVMMetadataWrapper(
+ LLVMDIBuilderCreateAutoVariable(
+ m_ref, scope.m_ref, name.c_str(), name.size(), file.m_ref, line_no,
+ di_type, always_preserve, (LLVMDIFlags)flags, align_in_bits),
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10144,7 +11772,7 @@ LLVMDIBuilderWrapper::create_global_variable_expression(
linkage.c_str(), linkage.size(), file.m_ref,
line_no, type.m_ref, is_local_to_unit,
expr.m_ref, decl_ref, align_in_bits),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10155,14 +11783,14 @@ LLVMDIBuilderWrapper::create_expression(const Iterable &addr) {
std::vector addr_copy = addr;
return LLVMMetadataWrapper(
LLVMDIBuilderCreateExpression(m_ref, addr_copy.data(), addr_copy.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
LLVMDIBuilderWrapper::create_constant_value_expression(uint64_t value) {
check_valid();
return LLVMMetadataWrapper(
- LLVMDIBuilderCreateConstantValueExpression(m_ref, value), m_module_token);
+ LLVMDIBuilderCreateConstantValueExpression(m_ref, value), m_context_token, m_ctx_ref, m_module_token);
}
// Label Methods
@@ -10175,7 +11803,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_label(
return LLVMMetadataWrapper(
LLVMDIBuilderCreateLabel(m_ref, scope.m_ref, name.c_str(), name.size(),
file.m_ref, line_no, always_preserve),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline void
@@ -10238,7 +11866,7 @@ inline LLVMMetadataWrapper
LLVMDIBuilderWrapper::get_or_create_subrange(int64_t lo, int64_t count) {
check_valid();
return LLVMMetadataWrapper(LLVMDIBuilderGetOrCreateSubrange(m_ref, lo, count),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_array(
@@ -10252,7 +11880,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_array(
}
return LLVMMetadataWrapper(
LLVMDIBuilderGetOrCreateArray(m_ref, elem_refs.data(), elem_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Enumerator Methods
@@ -10263,7 +11891,7 @@ LLVMDIBuilderWrapper::create_enumerator(const std::string &name, int64_t value,
return LLVMMetadataWrapper(LLVMDIBuilderCreateEnumerator(m_ref, name.c_str(),
name.size(), value,
is_unsigned),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10274,7 +11902,7 @@ LLVMDIBuilderWrapper::create_enumerator_of_arbitrary_precision(
return LLVMMetadataWrapper(LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision(
m_ref, name.c_str(), name.size(),
value.size() * 64, value.data(), is_unsigned),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// ObjC & Inheritance Methods
@@ -10290,7 +11918,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_objc_property(
m_ref, name.c_str(), name.size(), file.m_ref, line_no,
getter_name.c_str(), getter_name.size(), setter_name.c_str(),
setter_name.size(), property_attributes, type.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_objc_ivar(
@@ -10307,7 +11935,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_objc_ivar(
line_no, size_in_bits, align_in_bits,
offset_in_bits, (LLVMDIFlags)flags,
type.m_ref, property.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_inheritance(
@@ -10321,7 +11949,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_inheritance(
LLVMDIBuilderCreateInheritance(m_ref, derived_type.m_ref, base_type.m_ref,
offset_in_bits, v_bptr_offset,
(LLVMDIFlags)flags),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Import & Macro Methods
@@ -10344,7 +11972,7 @@ LLVMDIBuilderWrapper::create_imported_module_from_module(
m_ref, scope.m_ref, import_module.m_ref,
file.m_ref, line, elem_refs.data(),
elem_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10366,7 +11994,7 @@ LLVMDIBuilderWrapper::create_imported_module_from_alias(
m_ref, scope.m_ref, imported_entity.m_ref,
file.m_ref, line, elem_refs.data(),
elem_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_temp_macro_file(
@@ -10378,7 +12006,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_temp_macro_file(
parent_macro_file ? parent_macro_file->m_ref : nullptr;
return LLVMMetadataWrapper(
LLVMDIBuilderCreateTempMacroFile(m_ref, parent, line, file.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_macro(
@@ -10391,7 +12019,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_macro(
(LLVMDWARFMacinfoRecordType)macro_type,
name.c_str(), name.size(), value.c_str(),
value.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Missing DIBuilder Method Implementations
@@ -10417,7 +12045,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_member_type(
name.size(), file.m_ref, line_no,
size_in_bits, align_in_bits, offset_in_bits,
(LLVMDIFlags)flags, type.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_union_type(
@@ -10441,7 +12069,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_union_type(
line_number, size_in_bits, align_in_bits, (LLVMDIFlags)flags,
elem_refs.data(), elem_refs.size(), runtime_lang, unique_id.c_str(),
unique_id.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_array_type(
@@ -10460,7 +12088,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_array_type(
LLVMDIBuilderCreateArrayType(m_ref, size_in_bits, align_in_bits,
element_type.m_ref, sub_refs.data(),
sub_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10469,7 +12097,7 @@ LLVMDIBuilderWrapper::create_qualified_type(unsigned tag,
check_valid();
type.check_valid();
return LLVMMetadataWrapper(
- LLVMDIBuilderCreateQualifiedType(m_ref, tag, type.m_ref), m_module_token);
+ LLVMDIBuilderCreateQualifiedType(m_ref, tag, type.m_ref), m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10478,13 +12106,13 @@ LLVMDIBuilderWrapper::create_reference_type(unsigned tag,
check_valid();
type.check_valid();
return LLVMMetadataWrapper(
- LLVMDIBuilderCreateReferenceType(m_ref, tag, type.m_ref), m_module_token);
+ LLVMDIBuilderCreateReferenceType(m_ref, tag, type.m_ref), m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_null_ptr_type() {
check_valid();
return LLVMMetadataWrapper(LLVMDIBuilderCreateNullPtrType(m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_bit_field_member_type(
@@ -10501,7 +12129,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_bit_field_member_type(
file.m_ref, line_no, size_in_bits,
offset_in_bits, storage_offset_in_bits,
(LLVMDIFlags)flags, type.m_ref),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10509,7 +12137,7 @@ LLVMDIBuilderWrapper::create_artificial_type(const LLVMMetadataWrapper &type) {
check_valid();
type.check_valid();
return LLVMMetadataWrapper(
- LLVMDIBuilderCreateArtificialType(m_ref, type.m_ref), m_module_token);
+ LLVMDIBuilderCreateArtificialType(m_ref, type.m_ref), m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_type_array(
@@ -10523,7 +12151,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_type_array(
}
return LLVMMetadataWrapper(LLVMDIBuilderGetOrCreateTypeArray(
m_ref, type_refs.data(), type_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_lexical_block_file(
@@ -10534,7 +12162,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_lexical_block_file(
file.check_valid();
return LLVMMetadataWrapper(LLVMDIBuilderCreateLexicalBlockFile(
m_ref, scope.m_ref, file.m_ref, discriminator),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_imported_declaration(
@@ -10555,7 +12183,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_imported_declaration(
m_ref, scope.m_ref, decl.m_ref, file.m_ref,
line, name.c_str(), name.size(),
elem_refs.data(), elem_refs.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
inline LLVMMetadataWrapper
@@ -10569,7 +12197,7 @@ LLVMDIBuilderWrapper::create_imported_module_from_namespace(
return LLVMMetadataWrapper(
LLVMDIBuilderCreateImportedModuleFromNamespace(
m_ref, scope.m_ref, ns.m_ref, file.m_ref, line),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Utility Methods
@@ -10623,7 +12251,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_class_type(
vtable_holder ? vtable_holder->m_ref : nullptr,
template_params ? template_params->m_ref : nullptr, unique_id.c_str(),
unique_id.size()),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Create static member type debug info
@@ -10644,7 +12272,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_static_member_type(
m_ref, scope.m_ref, name.c_str(), name.size(), file.m_ref, line_no,
type.m_ref, (LLVMDIFlags)flags,
const_val ? const_val->m_ref : nullptr, align_in_bits),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Create member pointer type debug info (C++ pointer-to-member)
@@ -10660,7 +12288,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_member_pointer_type(
LLVMDIBuilderCreateMemberPointerType(m_ref, pointee_type.m_ref,
class_type.m_ref, size_in_bits,
align_in_bits, (LLVMDIFlags)flags),
- m_module_token);
+ m_context_token, m_ctx_ref, m_module_token);
}
// Insert declare record before an instruction
@@ -10722,8 +12350,8 @@ struct LLVMDIBuilderManager : NoMoveCopy {
if (!m_module_token || !m_module_token->is_valid())
throw LLVMMemoryError("DIBuilder's module has been destroyed");
m_module->check_valid();
- m_dibuilder = std::make_unique(m_module->m_ref,
- m_module->m_token);
+ m_dibuilder = std::make_unique(
+ m_module->m_ref, m_module->m_context_token, m_module->m_token);
m_entered = true;
return *m_dibuilder;
}
@@ -10733,9 +12361,9 @@ struct LLVMDIBuilderManager : NoMoveCopy {
throw LLVMMemoryError("DIBuilder has already been disposed");
if (!m_entered)
throw LLVMMemoryError("DIBuilder manager was not entered");
- // Finalize is typically called explicitly, but we could auto-finalize here
- // For now, just clean up - user should call finalize() before exiting
- m_dibuilder.reset();
+ // Finalize is typically called explicitly; users should call finalize()
+ // before exiting when debug info must be complete.
+ m_dibuilder->dispose();
m_disposed = true;
}
@@ -10860,7 +12488,8 @@ inline LLVMModuleWrapper *LLVMBasicBlockWrapper::module() const {
if (!mod_ref)
throw LLVMAssertionError("BasicBlock's function has no parent module");
LLVMContextRef ctx_ref = LLVMGetModuleContext(mod_ref);
- return LLVMModuleWrapper::create_borrowed(mod_ref, ctx_ref, m_context_token);
+ return LLVMModuleWrapper::create_borrowed(mod_ref, ctx_ref, m_context_token,
+ m_module_token);
}
inline LLVMContextWrapper *LLVMBasicBlockWrapper::context() const {
@@ -10886,7 +12515,8 @@ inline LLVMModuleWrapper *LLVMFunctionWrapper::module() const {
throw LLVMAssertionError("Function has no parent module");
LLVMContextRef ctx_ref = LLVMGetModuleContext(mod_ref);
// Return a non-owning (borrowed) wrapper for the function's module
- return LLVMModuleWrapper::create_borrowed(mod_ref, ctx_ref, m_context_token);
+ return LLVMModuleWrapper::create_borrowed(mod_ref, ctx_ref, m_context_token,
+ m_module_token);
}
inline LLVMContextWrapper *LLVMFunctionWrapper::context() const {
@@ -10959,7 +12589,8 @@ inline LLVMModuleWrapper *LLVMValueWrapper::get_module() const {
if (!mod_ref)
throw LLVMAssertionError("Value has no parent module");
LLVMContextRef ctx_ref = LLVMGetModuleContext(mod_ref);
- return LLVMModuleWrapper::create_borrowed(mod_ref, ctx_ref, m_context_token);
+ return LLVMModuleWrapper::create_borrowed(mod_ref, ctx_ref, m_context_token,
+ m_module_token);
}
inline LLVMContextWrapper *LLVMValueWrapper::get_context() const {
@@ -11002,6 +12633,14 @@ inline LLVMContextWrapper *LLVMValueWrapper::get_context() const {
// =============================================================================
NB_MODULE(llvm, m) {
+ // Initialize LLVM-C
+ LLVMInitializeAllTargetInfos();
+ LLVMInitializeAllTargets();
+ LLVMInitializeAllTargetMCs();
+ LLVMInitializeAllAsmPrinters();
+ LLVMInitializeAllAsmParsers();
+ LLVMInitializeAllDisassemblers();
+
// Register exceptions
auto exc_error = nb::exception(m, "LLVMError");
auto exc_memory =
@@ -11110,6 +12749,26 @@ NB_MODULE(llvm, m) {
"Appends the two values, dropping duplicates from the second list.")
.export_values();
+ nb::enum_(m, "DwarfLanguage")
+ .value("C89", LLVMDWARFSourceLanguageC89)
+ .value("C", LLVMDWARFSourceLanguageC)
+ .value("C99", LLVMDWARFSourceLanguageC99)
+ .value("C11", LLVMDWARFSourceLanguageC11)
+ .value("C17", LLVMDWARFSourceLanguageC17)
+ .value("CPlusPlus", LLVMDWARFSourceLanguageC_plus_plus)
+ .value("CPlusPlus11", LLVMDWARFSourceLanguageC_plus_plus_11)
+ .value("CPlusPlus14", LLVMDWARFSourceLanguageC_plus_plus_14)
+ .value("CPlusPlus17", LLVMDWARFSourceLanguageC_plus_plus_17)
+ .value("CPlusPlus20", LLVMDWARFSourceLanguageC_plus_plus_20)
+ .value("Python", LLVMDWARFSourceLanguagePython)
+ .value("Rust", LLVMDWARFSourceLanguageRust)
+ .value("Swift", LLVMDWARFSourceLanguageSwift);
+
+ nb::enum_(m, "DwarfEmissionKind")
+ .value("None_", LLVMDWARFEmissionNone)
+ .value("Full", LLVMDWARFEmissionFull)
+ .value("LineTablesOnly", LLVMDWARFEmissionLineTablesOnly);
+
nb::enum_(m, "Visibility")
.value("Default", LLVMDefaultVisibility)
.value("Hidden", LLVMHiddenVisibility)
@@ -11893,6 +13552,75 @@ Valid when:
C API: LLVMGetPreviousDbgRecord)");
+ nb::class_(m, "MetadataMap")
+ .def("__getitem__", &LLVMMetadataMapWrapper::get_item, "name"_a)
+ .def("__setitem__", &LLVMMetadataMapWrapper::set, "name"_a, "md"_a)
+ .def("__delitem__", &LLVMMetadataMapWrapper::erase, "name"_a)
+ .def("__contains__", &LLVMMetadataMapWrapper::contains, "name"_a)
+ .def("get", &LLVMMetadataMapWrapper::get, "name"_a,
+ R"(Get metadata by kind name, or None.)")
+ .def("remove", &LLVMMetadataMapWrapper::erase, "name"_a,
+ R"(Remove metadata by kind name.)")
+ .def("copy_to", &LLVMMetadataMapWrapper::copy_to, "target"_a,
+ "include_debug_location"_a = false,
+ R"(Copy all attached metadata to another instruction or global value.
+
+Instruction debug locations are copied only when include_debug_location=True.)");
+
+ nb::class_(m, "NamedMetadataList")
+ .def("__len__", &LLVMNamedMetadataListWrapper::size)
+ .def("__getitem__", &LLVMNamedMetadataListWrapper::get_item, "index"_a)
+ .def(
+ "__iter__",
+ [](const LLVMNamedMetadataListWrapper &self) {
+ nb::object items = nb::cast(self.all());
+ return nb::iter(items);
+ })
+ .def("append", &LLVMNamedMetadataListWrapper::append, "md"_a,
+ R"(Append a metadata node to this named metadata list.)")
+ .def("all", &LLVMNamedMetadataListWrapper::all,
+ R"(Return all operands as Metadata objects.)");
+
+ nb::class_(m, "NamedMetadataMap")
+ .def("__getitem__", &LLVMNamedMetadataMapWrapper::get_item, "name"_a,
+ R"(Return a named metadata list, creating it if needed.)")
+ .def("__contains__", &LLVMNamedMetadataMapWrapper::contains, "name"_a)
+ .def("get", &LLVMNamedMetadataMapWrapper::get, "name"_a,
+ R"(Return a named metadata list, or None if missing.)")
+ .def("keys", &LLVMNamedMetadataMapWrapper::keys,
+ R"(Return named metadata keys.)")
+ .def(
+ "__iter__",
+ [](const LLVMNamedMetadataMapWrapper &self) {
+ nb::object keys = nb::cast(self.keys());
+ return nb::iter(keys);
+ });
+
+ nb::class_(m, "ModuleFlags")
+ .def("add", &LLVMModuleFlagsWrapper::add, "key"_a, "behavior"_a,
+ "value"_a,
+ R"(Add a module flag by key.)")
+ .def("get", &LLVMModuleFlagsWrapper::get, "key"_a,
+ R"(Get a module flag by key, or None.)")
+ .def("__getitem__", &LLVMModuleFlagsWrapper::get_item, "key"_a)
+ .def("__contains__", &LLVMModuleFlagsWrapper::contains, "key"_a)
+ .def("keys", &LLVMModuleFlagsWrapper::keys,
+ R"(Return module flag keys.)")
+ .def(
+ "__iter__",
+ [](const LLVMModuleFlagsWrapper &self) {
+ nb::object keys = nb::cast(self.keys());
+ return nb::iter(keys);
+ });
+
+ nb::class_(m, "DebugLocationManager")
+ .def("__enter__", &LLVMBuilderDebugLocationManager::enter,
+ nb::rv_policy::reference_internal,
+ R"(Set the builder's current debug location.)")
+ .def("__exit__", &LLVMBuilderDebugLocationManager::exit,
+ "exc_type"_a.none(), "exc_value"_a.none(), "traceback"_a.none(),
+ R"(Restore the builder's previous debug location.)");
+
// Value wrapper
nb::class_(m, "Value")
.def("__eq__", [](const LLVMValueWrapper &a,
@@ -12500,7 +14228,7 @@ Returns True when the alloca size operand is not the constant 1.
C++ API parity: AllocaInst::isArrayAllocation)")
.def_prop_ro("function_type", &LLVMValueWrapper::get_function_type,
R"(Get function type.
-
+
C API: LLVMGlobalGetValueType)")
.def_prop_ro("value_kind", &LLVMValueWrapper::value_kind,
R"(Get value kind.
@@ -12723,6 +14451,10 @@ Alias for `.block`.
C API: LLVMGetTypeContext)",
nb::rv_policy::take_ownership)
+ .def_prop_ro("types", &LLVMValueWrapper::types,
+ R"(Type factory for this value's context.
+
+C API: LLVMTypeOf, LLVMGetTypeContext)")
// BasicBlock properties
.def_prop_ro("normal_dest", &LLVMValueWrapper::get_normal_dest,
R"(Get normal destination.
@@ -12780,17 +14512,6 @@ Alias for `.block`.
R"(Get the indices for GEP or ExtractValue/InsertValue instructions.
C API: LLVMGetIndices)")
- // Global/instruction metadata
- .def("global_copy_all_metadata",
- &LLVMValueWrapper::global_copy_all_metadata,
- R"(Copy all metadata.
-
-C API: LLVMGlobalCopyAllMetadata)")
- .def("instruction_get_all_metadata_other_than_debug_loc",
- &LLVMValueWrapper::instruction_get_all_metadata_other_than_debug_loc,
- R"(Get all metadata except debug loc.
-
-C API: LLVMInstructionGetAllMetadataOtherThanDebugLoc)")
// Value methods
.def("const_bitcast", &LLVMValueWrapper::const_bitcast, "type"_a,
R"(Create constant bitcast.
@@ -12810,6 +14531,15 @@ Alias for `.block`.
R"(Convert to metadata.
C API: LLVMValueAsMetadata)")
+ .def_prop_ro("metadata", &LLVMValueWrapper::metadata,
+ R"(Metadata mapping view for this instruction or global value.
+
+Use metadata kind names instead of numeric kind IDs:
+ inst.metadata["llvm.loop"] = md
+ md = inst.metadata.get("llvm.loop")
+ del inst.metadata["llvm.loop"]
+
+C API: LLVMGetMDKindIDInContext, LLVMSetMetadata, LLVMGlobalSetMetadata)")
.def("delete_instruction", &LLVMValueWrapper::delete_instruction,
R"(Delete this instruction from its parent block.
@@ -12901,12 +14631,6 @@ TODO: needs safe LLVM-C API equivalent.
},
"index"_a,
R"(Attributes for a callsite argument, using a 0-based Python index.)")
- // Unified metadata method
- .def("set_metadata", &LLVMValueWrapper::set_metadata, "kind"_a, "md"_a,
- "ctx"_a,
- R"(Set metadata on value.
-
-C API: LLVMSetMetadata, LLVMGlobalSetMetadata)")
// Builder creation for instructions.
.def("create_builder", &LLVMValueWrapper::create_builder, nb::kw_only(),
"before_dbg"_a = false, nb::rv_policy::take_ownership,
@@ -13006,6 +14730,10 @@ Returns None when the block has no non-PHI instruction.)")
C API: LLVMGetTypeContext)",
nb::rv_policy::take_ownership)
+ .def_prop_ro("types", &LLVMBasicBlockWrapper::types,
+ R"(Type factory for this basic block's context.
+
+C API: LLVMBasicBlockAsValue, LLVMTypeOf, LLVMGetTypeContext)")
.def_prop_ro("successors", &LLVMBasicBlockWrapper::successors,
R"(Successor blocks.
@@ -13234,24 +14962,55 @@ Valid when:
C API: LLVMGetTypeContext)",
nb::rv_policy::take_ownership)
+ .def_prop_ro("types", &LLVMFunctionWrapper::types,
+ R"(Type factory for this function's context.
+
+C API: LLVMTypeOf, LLVMGetTypeContext)")
+ .def("create_builder", &LLVMFunctionWrapper::create_builder,
+ nb::kw_only(), "first_non_phi"_a = false,
+ nb::rv_policy::take_ownership,
+ R"(Create a Builder in this function's entry block.
+
+If this function has no basic blocks yet, creates an entry block first.
+
+With `first_non_phi=True`, positions before the first non-PHI instruction in
+the entry block. If the entry block has no non-PHI instruction, the builder is
+positioned at the end of the block.
+
+Returns a BuilderManager for use with Python's 'with' statement.
+
+C API: LLVMAppendBasicBlockInContext, LLVMCreateBuilderInContext)")
// Verification
.def("verify", &LLVMFunctionWrapper::verify,
R"(Verify function.
C API: LLVMVerifyFunction
-
+
Returns True if the function is valid, False otherwise.
C API: LLVMVerifyFunction)")
.def("verify_and_print", &LLVMFunctionWrapper::verify_and_print,
R"(Verify function and print errors to stderr.
C API: LLVMVerifyFunction)")
+ .def("optimize", &LLVMFunctionWrapper::optimize, "pipeline"_a,
+ "target_machine"_a.none() = nullptr,
+ "options"_a.none() = nullptr,
+ R"doc(Optimize this function with an LLVM PassBuilder function pipeline string.
+
+Args:
+ pipeline: Function pass pipeline string (e.g., 'instcombine,simplifycfg').
+ target_machine: Optional target machine for target-specific passes.
+ options: Optional PassBuilderOptions.
+
+This mutates the function in place.
+
+C API: LLVMRunPassesOnFunction)doc")
// =====================================================================
// Intrinsic functions
// =====================================================================
.def_prop_ro("intrinsic_id", &LLVMFunctionWrapper::intrinsic_id,
R"(Get the intrinsic ID for this function.
-
+
Returns 0 if the function is not an intrinsic.
C API: LLVMGetIntrinsicID)")
@@ -13281,7 +15040,7 @@ Valid when reading:
.def_prop_rw("gc", &LLVMFunctionWrapper::get_gc,
&LLVMFunctionWrapper::set_gc,
R"(Get or set the GC name for this function.
-
+
Returns None if no GC is set.
C API: LLVMGetGC, LLVMSetGC)")
@@ -13344,6 +15103,30 @@ Valid when:
R"(Get the context this builder was created in.
C API: LLVMCreateBuilderInContext)")
+ .def("debug_location",
+ nb::overload_cast(
+ &LLVMBuilderWrapper::debug_location, nb::const_),
+ "loc"_a, nb::rv_policy::take_ownership,
+ R"(Temporarily set this builder's current debug location.
+
+Use with a with-statement:
+ with builder.debug_location(loc):
+ inst = builder.add(a, b)
+
+C API: LLVMSetCurrentDebugLocation2)")
+ .def("debug_location",
+ nb::overload_cast(
+ &LLVMBuilderWrapper::debug_location, nb::const_),
+ nb::kw_only(), "line"_a, "column"_a, "scope"_a,
+ "inlined_at"_a.none() = nullptr, nb::rv_policy::take_ownership,
+ R"(Create and temporarily set this builder's current debug location.
+
+Use with a with-statement:
+ with builder.debug_location(line=12, column=4, scope=subprogram):
+ inst = builder.add(a, b)
+
+C API: LLVMDIBuilderCreateDebugLocation, LLVMSetCurrentDebugLocation2)")
// Arithmetic
.def("add", &LLVMBuilderWrapper::add, "lhs"_a, "rhs"_a, "name"_a = "",
R"(Build add.
@@ -13473,12 +15256,18 @@ Valid when:
C API: LLVMBuildBinOp)")
// Memory
- .def("alloca", &LLVMBuilderWrapper::build_alloca, "ty"_a, "name"_a = "",
- R"(Build alloca.
+ .def("alloca",
+ nb::overload_cast(
+ &LLVMBuilderWrapper::build_alloca),
+ "ty"_a, "name"_a = "",
+ R"(Build scalar alloca.
C API: LLVMBuildAlloca)")
- .def("array_alloca", &LLVMBuilderWrapper::build_array_alloca, "ty"_a,
- "size"_a, "name"_a = "",
+ .def("alloca",
+ nb::overload_cast(
+ &LLVMBuilderWrapper::build_alloca),
+ "ty"_a, "count"_a, "name"_a = "",
R"(Build array alloca.
C API: LLVMBuildArrayAlloca)")
@@ -13644,6 +15433,33 @@ For indirect calls through raw pointers, use the explicit-type overload.
Prefer the 2-arg form call(func, args) for direct calls.
C API: LLVMBuildCall2)")
+ .def("intrinsic",
+ [](LLVMBuilderWrapper &self, const std::string &name,
+ const Iterable &args,
+ const std::string &name_hint) {
+ return self.intrinsic(name, args, Iterable(),
+ name_hint);
+ },
+ "name"_a, "args"_a, nb::kw_only(), "name_hint"_a = "",
+ R"(Build a call to a non-overloaded LLVM intrinsic by intrinsic name.
+
+Args:
+ name: Intrinsic name, such as "llvm.trap".
+ args: Call operands.
+ name_hint: Optional result name for non-void intrinsics.
+
+C API: LLVMLookupIntrinsicID, LLVMGetIntrinsicDeclaration, LLVMBuildCall2)")
+ .def("intrinsic", &LLVMBuilderWrapper::intrinsic, "name"_a, "args"_a,
+ nb::kw_only(), "overloaded_types"_a, "name_hint"_a = "",
+ R"(Build a call to an overloaded LLVM intrinsic by intrinsic name.
+
+Args:
+ name: Intrinsic name, such as "llvm.sqrt" or "llvm.memcpy".
+ args: Call operands.
+ overloaded_types: LLVM overload-disambiguation types for overloaded intrinsics.
+ name_hint: Optional result name for non-void intrinsics.
+
+C API: LLVMLookupIntrinsicID, LLVMGetIntrinsicDeclaration, LLVMBuildCall2)")
.def("unreachable", &LLVMBuilderWrapper::unreachable,
R"(Build unreachable.
@@ -13873,6 +15689,25 @@ Examples:
Attribute.enum(ctx, "noreturn")
Attribute.enum(ctx, "align", 16)
+C API: LLVMCreateEnumAttribute)")
+ .def_static(
+ "memory",
+ [](LLVMContextWrapper &ctx, const std::string &effects) {
+ return ctx.create_enum_attribute("memory",
+ encode_memory_effects(effects));
+ },
+ "context"_a, "effects"_a = "none",
+ R"(Create a memory(...) enum attribute.
+
+The effects string accepts "none", "read", "write", "readwrite", or
+location-specific entries such as "argmem: read". LLVM-version-specific memory
+locations such as errnomem are encoded when supported by the linked LLVM.
+
+Examples:
+ Attribute.memory(ctx, "none")
+ Attribute.memory(ctx, "read")
+ Attribute.memory(ctx, "argmem: read")
+
C API: LLVMCreateEnumAttribute)")
.def_static(
"type",
@@ -13970,6 +15805,20 @@ Examples:
attrs.add("align", 16)
C API: LLVMAddAttributeAtIndex / LLVMAddCallSiteAttribute)")
+ .def("add_memory", &LLVMAttributeAccessorWrapper::add_memory,
+ "effects"_a = "none",
+ R"(Add a memory(...) enum attribute.
+
+The effects string accepts "none", "read", "write", "readwrite", or
+location-specific entries such as "argmem: read". LLVM-version-specific memory
+locations such as errnomem are encoded when supported by the linked LLVM.
+
+Examples:
+ attrs.add_memory("none")
+ attrs.add_memory("read")
+ attrs.add_memory("argmem: read")
+
+C API: LLVMCreateEnumAttribute, LLVMAddAttributeAtIndex / LLVMAddCallSiteAttribute)")
.def("add_type", &LLVMAttributeAccessorWrapper::add_type, "name"_a,
"type"_a,
R"(Add a type attribute by name.)")
@@ -13998,42 +15847,6 @@ removes a string attribute with the same key.)")
C API: LLVMGetComdatSelectionKind, LLVMSetComdatSelectionKind)");
- // Value metadata entries wrapper (for global/instruction metadata copying)
- nb::class_(m, "ValueMetadataEntries")
- .def("__len__", &LLVMValueMetadataEntriesWrapper::size)
- .def("get_kind", &LLVMValueMetadataEntriesWrapper::get_kind, "index"_a,
- R"(Get metadata kind at index.
-
-C API: LLVMValueMetadataEntriesGetKind)")
- .def("get_metadata", &LLVMValueMetadataEntriesWrapper_get_metadata,
- "index"_a,
- R"(Get metadata at index.
-
-C API: LLVMValueMetadataEntriesGetMetadata)");
-
- // Named metadata node wrapper
- nb::class_(m, "NamedMDNode")
- .def("__eq__", [](const LLVMNamedMDNodeWrapper &a,
- const LLVMNamedMDNodeWrapper &b) { return a == b; })
- .def("__ne__", [](const LLVMNamedMDNodeWrapper &a,
- const LLVMNamedMDNodeWrapper &b) { return a != b; })
- .def("__hash__",
- [](const LLVMNamedMDNodeWrapper &v) {
- return std::hash{}(v.m_ref);
- })
- .def_prop_ro("name", &LLVMNamedMDNodeWrapper::get_name,
- R"(Get name.
-
-C API: LLVMGetNamedMetadataName)")
- .def_prop_ro("next", &LLVMNamedMDNodeWrapper::next,
- R"(Get next.
-
-C API: LLVMGetNextNamedMetadata)")
- .def_prop_ro("prev", &LLVMNamedMDNodeWrapper::prev,
- R"(Get previous.
-
-C API: LLVMGetPreviousNamedMetadata)");
-
// Module wrapper
nb::class_(m, "Module")
.def("__eq__", [](const LLVMModuleWrapper &a,
@@ -14207,40 +16020,6 @@ address space, and resolver match. Pass reuse_existing=False for LLVM's raw
inserting behavior.
C API: LLVMGetNamedGlobalIFunc, LLVMAddGlobalIFunc)")
- // Named metadata support
- .def_prop_ro("first_named_metadata",
- &LLVMModuleWrapper::first_named_metadata,
- R"(First named metadata.
-
-C API: LLVMGetFirstNamedMetadata)")
- .def_prop_ro("last_named_metadata",
- &LLVMModuleWrapper::last_named_metadata,
- R"(Last named metadata.
-
-C API: LLVMGetLastNamedMetadata)")
- .def("get_named_metadata", &LLVMModuleWrapper::get_named_metadata,
- "name"_a, R"(Get named metadata.
-
-C API: LLVMGetNamedMetadata)")
- .def("add_named_metadata",
- &LLVMModuleWrapper::add_named_metadata, "name"_a,
- R"(Add named metadata, or return the existing node with this name.
-
-This method has get-or-insert behavior: if named metadata with `name` already
-exists in the module, it is returned unchanged; otherwise a new empty named
-metadata node is created and returned.
-
-C API: LLVMGetOrInsertNamedMetadata)")
- .def("get_named_metadata_num_operands",
- &LLVMModuleWrapper::get_named_metadata_num_operands, "name"_a,
- R"(Get operand count.
-
-C API: LLVMGetNamedMetadataNumOperands)")
- .def("get_named_metadata_operands",
- &LLVMModuleWrapper::get_named_metadata_operands, "name"_a,
- R"(Get operands.
-
-C API: LLVMGetNamedMetadataOperands)")
// Inline assembly support
.def_prop_rw("inline_asm", &LLVMModuleWrapper::get_inline_asm,
&LLVMModuleWrapper::set_inline_asm,
@@ -14255,7 +16034,7 @@ metadata node is created and returned.
.def("write_bitcode_to_file", &LLVMModuleWrapper::write_bitcode_to_file,
"path"_a,
R"(Write the module as bitcode to a file.
-
+
Args:
path: Output file path
@@ -14263,7 +16042,7 @@ metadata node is created and returned.
.def("write_bitcode_to_memory_buffer",
&LLVMModuleWrapper::write_bitcode_to_memory_buffer,
R"(Write the module as bitcode to a bytes object.
-
+
Returns:
bytes: The bitcode data.
@@ -14271,9 +16050,9 @@ metadata node is created and returned.
// Linker methods
.def("link_module", &LLVMModuleWrapper::link_module, "src"_a,
R"(Link another module into this module.
-
+
The source module is destroyed after linking.
-
+
Args:
src: Source module to link in
@@ -14299,45 +16078,34 @@ Returns:
// Module printing to file
.def("print_to_file", &LLVMModuleWrapper::print_to_file, "filename"_a,
R"(Print the module IR to a file.
-
+
Args:
filename: Output file path
C API: LLVMPrintModuleToFile)")
- // Named metadata operand
- .def("add_named_metadata_operand",
- &LLVMModuleWrapper::add_named_metadata_operand, "name"_a, "md"_a,
- R"(Add operand.
+ .def_prop_ro("named_metadata", &LLVMModuleWrapper::named_metadata,
+ R"(Named metadata mapping view.
-C API: LLVMAddNamedMetadataOperand)")
- // =====================================================================
- // Module Flags API
- // =====================================================================
- .def("add_module_flag", &LLVMModuleWrapper::add_module_flag, "behavior"_a,
- "key"_a, "val"_a,
- R"(Add a module-level flag.
+Example:
+ mod.named_metadata["llvm.dbg.cu"].append(compile_unit)
-Args:
- behavior: The merge behavior (ModuleFlagBehavior enum)
- key: The flag name
- val: The metadata value
+C API: LLVMGetOrInsertNamedMetadata, LLVMAddNamedMetadataOperand)")
+ .def_prop_ro("module_flags", &LLVMModuleWrapper::module_flags,
+ R"(Module flags view keyed by flag name.
-C API: LLVMAddModuleFlag)")
- .def("get_module_flag", &LLVMModuleWrapper::get_module_flag, "key"_a,
- R"(Get a module-level flag by key.
+Example:
+ mod.module_flags.add("Debug Info Version", llvm.ModuleFlagBehavior.Warning, md)
-Args:
- key: The flag name
-
-Returns:
- The metadata value, or None if not found.
-
-C API: LLVMGetModuleFlag)")
+C API: LLVMAddModuleFlag, LLVMGetModuleFlag)")
// Module methods
.def_prop_ro("context", &LLVMModuleWrapper::get_context,
nb::rv_policy::take_ownership,
R"(Get the context for this module.
+C API: LLVMGetModuleContext)")
+ .def_prop_ro("types", &LLVMModuleWrapper::types,
+ R"(Type factory for this module's context.
+
C API: LLVMGetModuleContext)")
.def_prop_rw("is_new_dbg_info_format",
&LLVMModuleWrapper::is_new_dbg_info_format,
@@ -14362,6 +16130,43 @@ Args:
options: Optional PassBuilderOptions
C API: LLVMRunPasses)doc")
+ .def("optimize", &LLVMModuleWrapper::optimize, "pipeline"_a,
+ "target_machine"_a.none() = nullptr,
+ "options"_a.none() = nullptr,
+ R"doc(Optimize this module with an LLVM PassBuilder pipeline string.
+
+Args:
+ pipeline: Pass pipeline string (e.g., 'default').
+ target_machine: Optional target machine for target-specific passes.
+ options: Optional PassBuilderOptions.
+
+This mutates the module in place.
+
+C API: LLVMRunPasses)doc")
+ .def("emit_object", &LLVMModuleWrapper::emit_object,
+ "target_machine"_a.none() = nullptr,
+ R"doc(Emit this module to an object file.
+
+Args:
+ target_machine: Optional target machine. If omitted, a host target machine
+ is created internally.
+
+Returns:
+ bytes: The generated object file.
+
+C API: LLVMTargetMachineEmitToMemoryBuffer)doc")
+ .def("emit_assembly", &LLVMModuleWrapper::emit_assembly,
+ "target_machine"_a.none() = nullptr,
+ R"doc(Emit this module to assembly.
+
+Args:
+ target_machine: Optional target machine. If omitted, a host target machine
+ is created internally.
+
+Returns:
+ bytes: The generated assembly.
+
+C API: LLVMTargetMachineEmitToMemoryBuffer)doc")
.def("create_dibuilder", &LLVMModuleWrapper::create_dibuilder,
nb::rv_policy::take_ownership,
R"(Create a debug info builder for this module.
@@ -14395,6 +16200,14 @@ Returns a BuilderManager for use with Python's 'with' statement.
// TypeFactory wrapper (property-based type namespace)
nb::class_(m, "TypeFactory")
+ .def("__eq__", &LLVMTypeFactoryWrapper::operator==, "other"_a,
+ R"(Return True when both type factories use the same LLVM context.
+
+C API limitation: compares stored LLVMContextRef pointers)")
+ .def("__ne__", &LLVMTypeFactoryWrapper::operator!=, "other"_a,
+ R"(Return True when type factories use different LLVM contexts.
+
+C API limitation: compares stored LLVMContextRef pointers)")
// Fixed-width integer types
.def_prop_ro("i1", &LLVMTypeFactoryWrapper::i1,
R"(1-bit int.
@@ -14643,10 +16456,6 @@ Returns a BuilderManager for use with Python's 'with' statement.
R"(Create a struct constant in this context.
C API: LLVMConstStructInContext)")
- .def("get_md_kind_id", &LLVMContextWrapper::get_md_kind_id, "name"_a,
- R"(Get metadata kind ID.
-
-C API: LLVMGetMDKindIDInContext)")
// Metadata creation methods
.def("md_string", &LLVMContextWrapper::md_string, "str"_a,
R"(Create metadata string.
@@ -14670,8 +16479,9 @@ Common scope names include "singlethread" for thread-local synchronization.
R"(Get the function type of an intrinsic in this context.
C API: LLVMIntrinsicGetType)")
- .def("create_debug_location", &LLVMContextWrapper::create_debug_location,
- "line"_a, "column"_a, "scope"_a, "inlined_at"_a.none(),
+ .def("debug_location", &LLVMContextWrapper::debug_location,
+ "line"_a, "column"_a, "scope"_a,
+ "inlined_at"_a.none() = nullptr,
R"(Create a debug location metadata node.
C API: LLVMDIBuilderCreateDebugLocation)");
@@ -14781,12 +16591,25 @@ Valid when:
"create_operand_bundle",
[](const std::string &tag, const Iterable &args,
LLVMContextWrapper *ctx) {
+ if (!ctx)
+ throw LLVMMemoryError("create_operand_bundle requires a context");
+ ctx->check_valid();
std::vector arg_refs;
- for (const auto &a : args)
+ std::vector> module_tokens;
+ for (const auto &a : args) {
+ a.check_valid();
+ if (a.m_context_token != ctx->m_token) {
+ throw LLVMAssertionError(
+ "create_operand_bundle requires values from the context");
+ }
+ if (a.m_module_token)
+ module_tokens.push_back(a.m_module_token);
arg_refs.push_back(a.m_ref);
+ }
LLVMOperandBundleRef bundle = LLVMCreateOperandBundle(
tag.c_str(), tag.size(), arg_refs.data(), arg_refs.size());
- return new LLVMOperandBundleWrapper(bundle, ctx->m_token);
+ return new LLVMOperandBundleWrapper(bundle, ctx->m_token,
+ std::move(module_tokens));
},
"tag"_a, "args"_a, "ctx"_a, nb::rv_policy::take_ownership,
R"(Create operand bundle.
@@ -14839,68 +16662,11 @@ Raises LLVMError if the target is not found.
C API: LLVMGetTargetFromName)");
- // Target initialization functions are explicit so callers control process-wide
- // LLVM target registration.
- m.def("initialize_all", &initialize_all,
- R"(Initialize all targets, MCs, ASM printers, and ASM parsers.
-Convenience function that calls initialize_all_target_infos(),
-initialize_all_targets(), initialize_all_target_mcs(),
-initialize_all_asm_printers(), and initialize_all_asm_parsers().)");
- m.def("initialize_all_target_infos", &initialize_all_target_infos,
- R"(Initialize all target infos.
-
-C API: LLVMInitializeAllTargetInfos)");
- m.def("initialize_all_targets", &initialize_all_targets,
- R"(Initialize all targets.
-
-C API: LLVMInitializeAllTargets)");
- m.def("initialize_all_target_mcs", &initialize_all_target_mcs,
- R"(Initialize all target MCs.
-
-C API: LLVMInitializeAllTargetMCs)");
- m.def("initialize_all_asm_printers", &initialize_all_asm_printers,
- R"(Initialize all ASM printers.
-
-C API: LLVMInitializeAllAsmPrinters)");
- m.def("initialize_all_asm_parsers", &initialize_all_asm_parsers,
- R"(Initialize all ASM parsers.
-
-C API: LLVMInitializeAllAsmParsers)");
- m.def("initialize_all_disassemblers", &initialize_all_disassemblers,
- R"(Initialize all disassemblers.
-
-C API: LLVMInitializeAllDisassemblers)");
m.def("get_first_target", &get_first_target,
R"(Get the first registered target (returns None if no targets).
C API: LLVMGetFirstTarget)");
- // Native target initialization
- m.def("initialize_native_target", &initialize_native_target,
- R"(Initialize the native target.
-
- Returns True on success, False on failure.
-
-C API: LLVMInitializeNativeTarget)");
- m.def("initialize_native_asm_printer", &initialize_native_asm_printer,
- R"(Initialize the native ASM printer.
-
- Returns True on success, False on failure.
-
-C API: LLVMInitializeNativeAsmPrinter)");
- m.def("initialize_native_asm_parser", &initialize_native_asm_parser,
- R"(Initialize the native ASM parser.
-
- Returns True on success, False on failure.
-
-C API: LLVMInitializeNativeAsmParser)");
- m.def("initialize_native_disassembler", &initialize_native_disassembler,
- R"(Initialize the native disassembler.
-
- Returns True on success, False on failure.
-
-C API: LLVMInitializeNativeDisassembler)");
-
// Host target queries
m.attr("default_target_triple") = get_default_target_triple();
m.def("normalize_target_triple", &normalize_target_triple, "triple"_a,
@@ -14962,7 +16728,7 @@ initialize_all_asm_printers(), and initialize_all_asm_parsers().)");
nb::class_(m, "TargetData",
R"(Target data layout information.
-
+
Provides information about type sizes and alignment for a specific target.)")
.def("__str__", &LLVMTargetDataWrapper::to_string)
.def_prop_ro("byte_order", &LLVMTargetDataWrapper::byte_order,
@@ -15092,7 +16858,7 @@ Returns:
.def("emit_to_file", &LLVMTargetMachineWrapper::emit_to_file, "mod"_a,
"filename"_a, "file_type"_a,
R"(Emit the module to a file.
-
+
Args:
mod: Module to emit
filename: Output filename
@@ -15103,11 +16869,11 @@ Returns:
&LLVMTargetMachineWrapper::emit_to_memory_buffer, "mod"_a,
"file_type"_a,
R"(Emit the module to a memory buffer.
-
+
Args:
mod: Module to emit
file_type: Type of output (AssemblyFile or ObjectFile)
-
+
Returns:
bytes: The generated output.
@@ -15120,6 +16886,15 @@ Returns:
nb::rv_policy::take_ownership,
R"(Create a target machine for code generation.
+C API: LLVMCreateTargetMachine)")
+ .def_static("host", &create_host_target_machine, "cpu"_a = "",
+ "features"_a = "",
+ "opt_level"_a = LLVMCodeGenLevelDefault,
+ "reloc_mode"_a = LLVMRelocDefault,
+ "code_model"_a = LLVMCodeModelDefault,
+ nb::rv_policy::take_ownership,
+ R"(Create a target machine for the host target.
+
C API: LLVMCreateTargetMachine)");
// NOTE: target machine creation moved to TargetMachine.create().
@@ -15130,7 +16905,7 @@ Returns:
nb::class_(m, "PassBuilderOptions",
R"(Options for the pass builder.
-
+
Used to configure optimization passes when calling run_passes().)")
.def(nb::init<>(), R"(Create options.
@@ -15200,6 +16975,78 @@ Returns:
// NOTE: pass execution moved to Module.run_passes().
+ // ==========================================================================
+ // JIT Wrapper
+ // ==========================================================================
+
+ nb::class_(m, "JITCtypesFunction",
+ R"(Callable ctypes wrapper for a JIT symbol.)")
+ .def("__call__", &LLVMCtypesFunctionWrapper::call,
+ R"(Call the underlying ctypes function.
+
+Valid while the owning JIT has not been disposed.)")
+ .def_prop_ro("ctypes_callable", &LLVMCtypesFunctionWrapper::ctypes_callable,
+ R"(The underlying ctypes callable.)");
+
+ nb::class_(m, "JIT", R"(In-process LLVM JIT using LLVM-C ORC LLJIT.)")
+ .def_static("host", &LLVMJITWrapper::host, nb::rv_policy::take_ownership,
+ R"(Create a host JIT.
+
+Use with a context manager:
+ with llvm.JIT.host() as jit:
+ ...
+
+C API: LLVMOrcCreateLLJIT)")
+ .def("__enter__", &LLVMJITWrapper::enter, nb::rv_policy::reference_internal,
+ R"(Enter the JIT context manager.)")
+ .def("__exit__", &LLVMJITWrapper::exit, "exc_type"_a.none(),
+ "exc_value"_a.none(), "traceback"_a.none(),
+ R"(Dispose the JIT when leaving the context manager.)")
+ .def("dispose", &LLVMJITWrapper::dispose, R"(Dispose the JIT.)")
+ .def_prop_ro("triple", &LLVMJITWrapper::triple,
+ R"(Get this JIT's target triple.
+
+C API: LLVMOrcLLJITGetTripleString)")
+ .def_prop_ro("data_layout", &LLVMJITWrapper::data_layout,
+ R"(Get this JIT's data layout string.
+
+C API: LLVMOrcLLJITGetDataLayoutStr)")
+ .def("add_module", &LLVMJITWrapper::add_module, "mod"_a,
+ R"(Add a module to the JIT.
+
+This transfers ownership from the user's perspective: the Python Module wrapper
+is invalid after the call succeeds.
+
+C API: LLVMOrcLLJITAddLLVMIRModule)")
+ .def("lookup", &LLVMJITWrapper::lookup, "name"_a,
+ R"(Look up a JIT symbol address by name.
+
+Returns:
+ int: The symbol address.
+
+C API: LLVMOrcLLJITLookup)")
+ .def("ctypes_function", &LLVMJITWrapper::ctypes_function, "name"_a,
+ "restype"_a, "argtypes"_a, nb::rv_policy::take_ownership,
+ nb::keep_alive<0, 1>(),
+ R"(Return a callable ctypes wrapper for a JIT symbol.
+
+Args:
+ name: Symbol name to look up.
+ restype: ctypes return type.
+ argtypes: Iterable of ctypes argument types.
+
+The returned wrapper keeps the JIT object alive and checks that the JIT has
+not been disposed before calling.
+
+C API: LLVMOrcLLJITLookup)")
+ .def("add_symbol", &LLVMJITWrapper::add_symbol, "name"_a, "target"_a,
+ R"(Add an absolute symbol to the JIT.
+
+`target` may be a non-negative integer address or a ctypes object. ctypes
+objects are pinned by the JIT to keep callbacks alive.
+
+C API: LLVMOrcAbsoluteSymbols, LLVMOrcJITDylibDefine)");
+
// Memory buffer is internal only - not exposed to Python
// =============================================================================
@@ -15216,12 +17063,12 @@ Returns:
.def("disasm_instruction", &LLVMDisasmContextWrapper::disasm_instruction,
"bytes"_a, "offset"_a, "pc"_a,
R"(Disassemble a single instruction.
-
+
Args:
bytes: The byte array containing machine code
offset: Offset into bytes to start disassembling
pc: Program counter value for the instruction
-
+
Returns:
Tuple of (bytes_consumed, disassembly_string)
If bytes_consumed is 0, disassembly failed.
@@ -15334,9 +17181,9 @@ Valid when:
C API: LLVMObjectFileCopySymbolIterator)")
.def("copy_to_memory_buffer", &LLVMBinaryWrapper::copy_to_memory_buffer,
R"(Copy the binary's contents to a memory buffer.
-
+
Returns a copy of the binary's backing memory buffer as bytes.
-
+
Returns:
bytes: A copy of the binary data.
@@ -15431,8 +17278,11 @@ Valid when:
self.m_binary_token);
},
nb::rv_policy::take_ownership,
+ nb::keep_alive<0, 1>(),
R"(Relocation iterator.
+The returned relocation iterator keeps this section iterator alive.
+
Valid when:
- parent binary is still valid (not disposed)
- section iterator is not at end (`is_at_end() == False`)
@@ -15445,14 +17295,15 @@ Valid when:
"__next__",
[](LLVMSectionIteratorWrapper &self) -> LLVMSectionIteratorWrapper * {
self.check_valid();
+ if (self.is_at_end())
+ throw nb::stop_iteration();
if (self.m_python_iter_started) {
self.move_next();
+ if (self.is_at_end())
+ throw nb::stop_iteration();
} else {
self.m_python_iter_started = true;
}
- if (self.is_at_end()) {
- throw nb::stop_iteration();
- }
return &self;
},
nb::rv_policy::reference_internal);
@@ -15510,14 +17361,15 @@ TODO: needs safe LLVM-C API equivalent.
"__next__",
[](LLVMSymbolIteratorWrapper &self) -> LLVMSymbolIteratorWrapper * {
self.check_valid();
+ if (self.is_at_end())
+ throw nb::stop_iteration();
if (self.m_python_iter_started) {
self.move_next();
+ if (self.is_at_end())
+ throw nb::stop_iteration();
} else {
self.m_python_iter_started = true;
}
- if (self.is_at_end()) {
- throw nb::stop_iteration();
- }
return &self;
},
nb::rv_policy::reference_internal);
@@ -15580,14 +17432,15 @@ Valid when:
[](LLVMRelocationIteratorWrapper &self)
-> LLVMRelocationIteratorWrapper * {
self.check_valid();
+ if (self.is_at_end())
+ throw nb::stop_iteration();
if (self.m_python_iter_started) {
self.move_next();
+ if (self.is_at_end())
+ throw nb::stop_iteration();
} else {
self.m_python_iter_started = true;
}
- if (self.is_at_end()) {
- throw nb::stop_iteration();
- }
return &self;
},
nb::rv_policy::reference_internal);
@@ -15634,17 +17487,6 @@ Valid when:
// BitReader functions
- // Static metadata function
- m.def(
- "get_md_kind_id",
- [](const std::string &name) {
- return LLVMGetMDKindID(name.c_str(),
- static_cast(name.size()));
- },
- "name"_a, R"(Get metadata kind ID.
-
-C API: LLVMGetMDKindID)");
-
// NOTE: get_module_context has been moved to Module.context property
// ==========================================================================
@@ -15688,15 +17530,20 @@ Valid when:
C API: LLVMDIBuilderFinalize)")
// File & Scope Creation
- .def("create_file", &LLVMDIBuilderWrapper::create_file, "filename"_a,
- "directory"_a, R"(Create file debug info metadata.
+ .def("file", &LLVMDIBuilderWrapper::file, "filename"_a,
+ "directory"_a = ".",
+ R"(Create file debug info metadata.
C API: LLVMDIBuilderCreateFile)")
- .def("create_compile_unit", &LLVMDIBuilderWrapper::create_compile_unit,
- "lang"_a, "file"_a, "producer"_a, "is_optimized"_a, "flags"_a,
- "runtime_ver"_a, "split_name"_a, "kind"_a, "dwo_id"_a,
- "split_debug_inlining"_a, "debug_info_for_profiling"_a, "sys_root"_a,
- "sdk"_a, R"(Create compile unit debug info.
+ .def("compile_unit", &LLVMDIBuilderWrapper::compile_unit,
+ "language"_a, "file"_a, "producer"_a,
+ "is_optimized"_a = false, "flags"_a = "",
+ "runtime_ver"_a = 0, "split_name"_a = "",
+ "kind"_a = LLVMDWARFEmissionFull, "dwo_id"_a = 0,
+ "split_debug_inlining"_a = true,
+ "debug_info_for_profiling"_a = false, "sys_root"_a = "",
+ "sdk"_a = "",
+ R"(Create compile unit debug info with defaults for common use.
C API: LLVMDIBuilderCreateCompileUnit)")
.def("create_module", &LLVMDIBuilderWrapper::create_module,
@@ -15729,6 +17576,16 @@ Valid when:
R"(Create subroutine type debug info (function signature).
C API: LLVMDIBuilderCreateSubroutineType)")
+ .def("function", &LLVMDIBuilderWrapper::function, "fn"_a, "name"_a,
+ "file"_a, "line"_a, "return_type"_a, "param_types"_a,
+ "scope"_a.none() = nullptr, "linkage_name"_a = "",
+ "is_local_to_unit"_a = false, "is_definition"_a = true,
+ "scope_line"_a = 0, "flags"_a = 0, "is_optimized"_a = false,
+ R"(Create function debug info, set it on the LLVM function, and return it.
+
+This convenience method maps simple IR types to debug types.
+
+C API: LLVMDIBuilderCreateFunction, LLVMSetSubprogram)")
// Type Creation
.def("create_basic_type", &LLVMDIBuilderWrapper::create_basic_type,
"name"_a, "size_in_bits"_a, "encoding"_a, "flags"_a,
@@ -15816,6 +17673,13 @@ Valid when:
"always_preserve"_a, "flags"_a, "align_in_bits"_a,
R"(Create local (auto) variable debug info.
+C API: LLVMDIBuilderCreateAutoVariable)")
+ .def("local_variable", &LLVMDIBuilderWrapper::local_variable,
+ "scope"_a, "name"_a, "file"_a, "line_no"_a, "type"_a,
+ "always_preserve"_a = true, "flags"_a = 0,
+ "align_in_bits"_a = 0,
+ R"(Create local variable debug info from a simple IR type.
+
C API: LLVMDIBuilderCreateAutoVariable)")
.def("create_global_variable_expression",
&LLVMDIBuilderWrapper::create_global_variable_expression, "scope"_a,
@@ -16009,7 +17873,7 @@ Valid when:
"derived_from"_a.none(), "elements"_a, "vtable_holder"_a.none(),
"template_params"_a.none(), "unique_id"_a,
R"(Create a C++ class type.
-
+
Args:
scope: The scope containing this class.
name: Class name.
@@ -16031,9 +17895,9 @@ Valid when:
"name"_a, "file"_a, "line_no"_a, "type"_a, "flags"_a,
"const_val"_a.none(), "align_in_bits"_a,
R"(Create a static member type.
-
+
Used for C++ static class members.
-
+
Args:
scope: The scope containing this member.
name: Member name.
@@ -16049,9 +17913,9 @@ Valid when:
&LLVMDIBuilderWrapper::create_member_pointer_type, "pointee_type"_a,
"class_type"_a, "size_in_bits"_a, "align_in_bits"_a, "flags"_a,
R"(Create a C++ member pointer type.
-
+
Used for pointer-to-member types (e.g., int MyClass::*).
-
+
Args:
pointee_type: The type being pointed to.
class_type: The class type.
@@ -16065,9 +17929,9 @@ Valid when:
&LLVMDIBuilderWrapper::insert_declare_record_before, "storage"_a,
"var_info"_a, "expr"_a, "debug_loc"_a, "insert_before"_a,
R"(Insert a declare record before an instruction.
-
+
Only use in "new debug format" mode (LLVMIsNewDbgInfoFormat is true).
-
+
Args:
storage: The storage location (alloca).
var_info: Variable debug info.
@@ -16080,9 +17944,9 @@ Valid when:
&LLVMDIBuilderWrapper::insert_dbg_value_record_before, "val"_a,
"var_info"_a, "expr"_a, "debug_loc"_a, "insert_before"_a,
R"(Insert a dbg value record before an instruction.
-
+
Only use in "new debug format" mode (LLVMIsNewDbgInfoFormat is true).
-
+
Args:
val: The value to track.
var_info: Variable debug info.
@@ -16126,6 +17990,24 @@ Valid when:
// NOTE: create_dibuilder has been moved to Module.create_dibuilder() method
+ auto require_di_type_metadata = [](const LLVMMetadataWrapper &self,
+ const char *accessor) {
+ self.check_valid();
+ switch (LLVMGetMetadataKind(self.m_ref)) {
+ case LLVMDIBasicTypeMetadataKind:
+ case LLVMDIDerivedTypeMetadataKind:
+ case LLVMDICompositeTypeMetadataKind:
+ case LLVMDISubroutineTypeMetadataKind:
+ case LLVMDIStringTypeMetadataKind:
+ case LLVMDISubrangeTypeMetadataKind:
+ case LLVMDIFixedPointTypeMetadataKind:
+ return;
+ default:
+ throw LLVMAssertionError(std::string(accessor) +
+ " requires DIType metadata");
+ }
+ };
+
nb::class_(m, "Metadata")
.def("__eq__", [](const LLVMMetadataWrapper &a,
const LLVMMetadataWrapper &b) { return a.m_ref == b.m_ref; })
@@ -16135,10 +18017,6 @@ Valid when:
[](const LLVMMetadataWrapper &v) {
return std::hash{}(v.m_ref);
})
- .def("as_value", &LLVMMetadataWrapper::as_value, "ctx"_a,
- R"(Convert metadata to value.
-
-C API: LLVMMetadataAsValue)")
.def("replace_all_uses_with", &LLVMMetadataWrapper::replace_all_uses_with,
"md"_a,
R"(Replace all uses of this temporary metadata.
@@ -16149,6 +18027,40 @@ Valid when:
R"(Replace this DISubprogram's subroutine type metadata.
C API: LLVMDISubprogramReplaceType)")
+ .def_prop_ro("kind", &LLVMMetadataWrapper::kind,
+ R"(Return this metadata's LLVMMetadataKind value.
+
+C API: LLVMGetMetadataKind)")
+ .def_prop_ro("is_string", &LLVMMetadataWrapper::is_string,
+ R"(True if this metadata is an MDString.
+
+C API: LLVMGetMetadataKind)")
+ .def_prop_ro("is_node", &LLVMMetadataWrapper::is_node,
+ R"(True if this metadata is an MDNode.
+
+C API: LLVMMetadataAsValue, LLVMIsAMDNode)")
+ .def_prop_ro("is_value", &LLVMMetadataWrapper::is_value,
+ R"(True if this metadata wraps a Value.
+
+C API: LLVMGetMetadataKind)")
+ .def_prop_ro("string", &LLVMMetadataWrapper::string,
+ R"(Return this MDString's Python string value.
+
+Raises LLVMAssertionError when this metadata is not an MDString.
+
+C API: LLVMMetadataAsValue, LLVMGetMDString)")
+ .def_prop_ro("operands", &LLVMMetadataWrapper::operands,
+ R"(Return this MDNode's operands as Metadata objects.
+
+Raises LLVMAssertionError when this metadata is not an MDNode.
+
+C API: LLVMMetadataAsValue, LLVMGetMDNodeNumOperands, LLVMGetMDNodeOperands)")
+ .def_prop_ro("value", &LLVMMetadataWrapper::value,
+ R"(Return the Value wrapped by ConstantAsMetadata or LocalAsMetadata.
+
+Raises NotImplementedError because LLVM-C has no unwrap API for ValueAsMetadata.
+
+C API limitation)")
// DI accessors as properties
.def_prop_ro("di_node_tag", [](const LLVMMetadataWrapper &self) -> unsigned {
self.check_valid();
@@ -16156,14 +18068,44 @@ Valid when:
}, R"(Get DWARF tag from this debug info node.
C API: LLVMGetDINodeTag)")
- .def_prop_ro("di_type_name", [](const LLVMMetadataWrapper &self) -> std::string {
- self.check_valid();
+ .def_prop_ro("di_type_name", [require_di_type_metadata](const LLVMMetadataWrapper &self) -> std::string {
+ require_di_type_metadata(self, "di_type_name");
size_t len;
const char *name = LLVMDITypeGetName(self.m_ref, &len);
return std::string(name, len);
}, R"(Get name from this debug info type.
C API: LLVMDITypeGetName)")
+ .def_prop_ro("di_type_size_in_bits", [require_di_type_metadata](const LLVMMetadataWrapper &self) -> uint64_t {
+ require_di_type_metadata(self, "di_type_size_in_bits");
+ return LLVMDITypeGetSizeInBits(self.m_ref);
+ }, R"(Get size in bits from this debug info type.
+
+C API: LLVMDITypeGetSizeInBits)")
+ .def_prop_ro("di_type_offset_in_bits", [require_di_type_metadata](const LLVMMetadataWrapper &self) -> uint64_t {
+ require_di_type_metadata(self, "di_type_offset_in_bits");
+ return LLVMDITypeGetOffsetInBits(self.m_ref);
+ }, R"(Get offset in bits from this debug info type.
+
+C API: LLVMDITypeGetOffsetInBits)")
+ .def_prop_ro("di_type_align_in_bits", [require_di_type_metadata](const LLVMMetadataWrapper &self) -> uint32_t {
+ require_di_type_metadata(self, "di_type_align_in_bits");
+ return LLVMDITypeGetAlignInBits(self.m_ref);
+ }, R"(Get alignment in bits from this debug info type.
+
+C API: LLVMDITypeGetAlignInBits)")
+ .def_prop_ro("di_type_line", [require_di_type_metadata](const LLVMMetadataWrapper &self) -> unsigned {
+ require_di_type_metadata(self, "di_type_line");
+ return LLVMDITypeGetLine(self.m_ref);
+ }, R"(Get line from this debug info type.
+
+C API: LLVMDITypeGetLine)")
+ .def_prop_ro("di_type_flags", [require_di_type_metadata](const LLVMMetadataWrapper &self) -> unsigned {
+ require_di_type_metadata(self, "di_type_flags");
+ return LLVMDITypeGetFlags(self.m_ref);
+ }, R"(Get DI flags from this debug info type.
+
+C API: LLVMDITypeGetFlags)")
.def_prop_ro("di_location_line", [](const LLVMMetadataWrapper &self) -> unsigned {
self.check_valid();
return LLVMDILocationGetLine(self.m_ref);
@@ -16179,7 +18121,8 @@ Valid when:
.def_prop_ro("di_location_scope", [](const LLVMMetadataWrapper &self) -> LLVMMetadataWrapper {
self.check_valid();
return LLVMMetadataWrapper(LLVMDILocationGetScope(self.m_ref),
- self.m_context_token);
+ self.m_context_token, self.m_ctx_ref,
+ self.m_module_token);
}, R"(Get scope from this debug location.
C API: LLVMDILocationGetScope)")
@@ -16188,7 +18131,8 @@ Valid when:
self.check_valid();
LLVMMetadataRef ref = LLVMDILocationGetInlinedAt(self.m_ref);
if (ref)
- return LLVMMetadataWrapper(ref, self.m_context_token);
+ return LLVMMetadataWrapper(ref, self.m_context_token, self.m_ctx_ref,
+ self.m_module_token);
return std::nullopt;
}, R"(Get inlined-at location from this debug location, or None.
@@ -16198,7 +18142,8 @@ Valid when:
self.check_valid();
LLVMMetadataRef ref = LLVMDIScopeGetFile(self.m_ref);
if (ref)
- return LLVMMetadataWrapper(ref, self.m_context_token);
+ return LLVMMetadataWrapper(ref, self.m_context_token, self.m_ctx_ref,
+ self.m_module_token);
return std::nullopt;
}, R"(Get file from this debug scope, or None.
@@ -16238,7 +18183,8 @@ Valid when:
self.check_valid();
LLVMMetadataRef ref = LLVMDIVariableGetFile(self.m_ref);
if (ref)
- return LLVMMetadataWrapper(ref, self.m_context_token);
+ return LLVMMetadataWrapper(ref, self.m_context_token, self.m_ctx_ref,
+ self.m_module_token);
return std::nullopt;
}, R"(Get file from this debug variable, or None.
@@ -16248,7 +18194,8 @@ Valid when:
self.check_valid();
LLVMMetadataRef ref = LLVMDIVariableGetScope(self.m_ref);
if (ref)
- return LLVMMetadataWrapper(ref, self.m_context_token);
+ return LLVMMetadataWrapper(ref, self.m_context_token, self.m_ctx_ref,
+ self.m_module_token);
return std::nullopt;
}, R"(Get scope from this debug variable, or None.
@@ -16263,7 +18210,7 @@ Valid when:
self.check_valid();
return LLVMMetadataWrapper(
LLVMDIGlobalVariableExpressionGetVariable(self.m_ref),
- self.m_context_token);
+ self.m_context_token, self.m_ctx_ref, self.m_module_token);
}, R"(Get the DIGlobalVariable from this DIGlobalVariableExpression.
C API: LLVMDIGlobalVariableExpressionGetVariable)")
@@ -16271,7 +18218,7 @@ Valid when:
self.check_valid();
return LLVMMetadataWrapper(
LLVMDIGlobalVariableExpressionGetExpression(self.m_ref),
- self.m_context_token);
+ self.m_context_token, self.m_ctx_ref, self.m_module_token);
}, R"(Get the DIExpression from this DIGlobalVariableExpression.
C API: LLVMDIGlobalVariableExpressionGetExpression)");
@@ -16290,10 +18237,10 @@ Valid when:
},
"name"_a,
R"(Look up an intrinsic ID by name.
-
+
Args:
name: Intrinsic name (e.g., "llvm.memcpy")
-
+
Returns:
Intrinsic ID (0 if not found).
@@ -16303,7 +18250,7 @@ Valid when:
"intrinsic_is_overloaded",
[](unsigned id) -> bool { return LLVMIntrinsicIsOverloaded(id); }, "id"_a,
R"(Check if an intrinsic is overloaded.
-
+
Overloaded intrinsics require type parameters to get a declaration.
C API: LLVMIntrinsicIsOverloaded)");
@@ -16322,7 +18269,7 @@ Valid when:
// NOTE: intrinsic type lookup moved to Context.get_intrinsic_type().
// NOTE: cast-opcode and metadata-node mutation helpers moved to Value methods.
- // NOTE: debug-location creation moved to Context.create_debug_location().
+ // NOTE: debug-location creation is exposed as Context.debug_location().
// NOTE: DISubprogram type replacement moved to Metadata.replace_di_subprogram_type().
// NOTE: debug record traversal moved to Value.first_dbg_record,
// Value.last_dbg_record, and DbgRecord.next/prev.
diff --git a/tests/regressions/test_api_surface_cleanup.py b/tests/regressions/test_api_surface_cleanup.py
deleted file mode 100644
index c580de0..0000000
--- a/tests/regressions/test_api_surface_cleanup.py
+++ /dev/null
@@ -1,300 +0,0 @@
-"""
-Regression coverage for API surface cleanup helpers.
-
-Covers:
-- Module add_* reuse_existing=True defaults and explicit raw insertion escape hatch
-- Named struct reuse_existing=True defaults and explicit raw insertion escape hatch
-- New member/factory APIs that replace module-level helpers
-- Builder parent navigation helpers and Module.create_builder()
-"""
-
-import llvm
-
-
-def expect_raises(fn, message_part: str) -> None:
- try:
- fn()
- except Exception as exc:
- assert message_part in str(exc), str(exc)
- else:
- raise AssertionError("expected exception")
-
-
-def test_module_add_reuse_existing_semantics() -> None:
- with llvm.create_context() as ctx:
- i32 = ctx.types.i32
- i64 = ctx.types.i64
- void = ctx.types.void
- fn_ty = ctx.types.function(void, [])
- other_fn_ty = ctx.types.function(i32, [])
- resolver_ty = ctx.types.function(i32, [])
-
- with ctx.create_module("reuse_existing") as mod:
- fn = mod.add_function("f", fn_ty)
- assert mod.add_function("f", fn_ty) == fn
- assert len([f for f in mod.functions if f.name.startswith("f")]) == 1
- expect_raises(lambda: mod.add_function("f", other_fn_ty), "different type")
- raw_fn = mod.add_function("f", fn_ty, reuse_existing=False)
- assert raw_fn.name != "f"
-
- g = mod.add_global(i32, "g")
- assert mod.add_global(i32, "g") == g
- expect_raises(lambda: mod.add_global(i64, "g"), "different type")
- expect_raises(
- lambda: mod.add_global_in_address_space(i32, "g", 1),
- "address space",
- )
- raw_g = mod.add_global(i32, "g", reuse_existing=False)
- assert raw_g.name != "g"
-
- expect_raises(lambda: mod.add_function("g", fn_ty), "global variable")
- expect_raises(lambda: mod.add_global(i32, "f"), "function")
-
- alias = mod.add_alias(i32, 0, g, "alias")
- assert mod.add_alias(i32, 0, g, "alias") == alias
- expect_raises(lambda: mod.add_alias(i64, 0, g, "alias"), "different type")
- raw_alias = mod.add_alias(i32, 0, g, "alias", reuse_existing=False)
- assert raw_alias.name != "alias"
-
- resolver = mod.add_function("resolver", resolver_ty)
- other_resolver = mod.add_function("other_resolver", resolver_ty)
- ifunc = mod.add_global_ifunc("ifunc", resolver_ty, 0, resolver)
- assert mod.add_global_ifunc("ifunc", resolver_ty, 0, resolver) == ifunc
- expect_raises(
- lambda: mod.add_global_ifunc("ifunc", resolver_ty, 1, resolver),
- "address space",
- )
- expect_raises(
- lambda: mod.add_global_ifunc("ifunc", resolver_ty, 0, other_resolver),
- "different resolver",
- )
- raw_ifunc = mod.add_global_ifunc(
- "ifunc", resolver_ty, 0, resolver, reuse_existing=False
- )
- assert raw_ifunc.name != "ifunc"
-
-
-def test_named_struct_reuse_existing_semantics() -> None:
- with llvm.create_context() as ctx:
- i32 = ctx.types.i32
- i64 = ctx.types.i64
-
- pair = ctx.types.struct("Pair", [i32, i64], packed=False)
- assert pair.struct_name == "Pair"
- assert not pair.is_opaque_struct
- assert ctx.types.struct("Pair", [i32, i64], packed=False) == pair
- expect_raises(
- lambda: ctx.types.struct("Pair", [i32], packed=False),
- "different body",
- )
- expect_raises(
- lambda: ctx.types.struct("Pair", [i32, i64], packed=True),
- "different packing",
- )
-
- raw_pair = ctx.types.struct(
- "Pair", [i32, i64], packed=False, reuse_existing=False
- )
- assert raw_pair != pair
- assert raw_pair.struct_name != "Pair"
-
- forward = ctx.types.opaque_struct("Forward")
- assert forward.is_opaque_struct
- assert ctx.types.opaque_struct("Forward") == forward
- completed = ctx.types.struct("Forward", [i32])
- assert completed == forward
- assert not completed.is_opaque_struct
-
- raw_forward = ctx.types.opaque_struct("Forward", reuse_existing=False)
- assert raw_forward != forward
- assert raw_forward.struct_name != "Forward"
-
-
-def test_member_factories_and_builder_navigation() -> None:
- llvm.initialize_all_target_infos()
- llvm.initialize_all_targets()
- llvm.initialize_all_target_mcs()
- llvm.initialize_all_asm_printers()
- llvm.initialize_all_asm_parsers()
-
- removed_globals = [
- "AttributeFunctionIndex",
- "AttributeReturnIndex",
- "last_enum_attribute_kind",
- "block_address",
- "const_data_array",
- "const_gep_with_no_wrap_flags",
- "const_int_of_arbitrary_precision",
- "const_ptr_auth",
- "const_string",
- "const_struct",
- "const_vector",
- "create_binary_from_bytes",
- "create_binary_from_file",
- "create_disasm_cpu_features",
- "create_target_data",
- "create_target_machine",
- "di_file_get_directory",
- "di_file_get_filename",
- "di_file_get_source",
- "di_global_variable_expression_get_expression",
- "di_global_variable_expression_get_variable",
- "di_location_get_column",
- "di_location_get_inlined_at",
- "di_location_get_line",
- "di_location_get_scope",
- "di_scope_get_file",
- "di_subprogram_get_line",
- "di_subprogram_replace_type",
- "di_type_get_name",
- "di_variable_get_file",
- "di_variable_get_line",
- "di_variable_get_scope",
- "dibuilder_create_debug_location",
- "get_cast_opcode",
- "get_debug_metadata_version",
- "get_default_target_triple",
- "get_first_dbg_record",
- "get_host_cpu_features",
- "get_host_cpu_name",
- "get_inline_asm",
- "get_di_node_tag",
- "get_last_dbg_record",
- "get_last_enum_attribute_kind",
- "get_module_debug_metadata_version",
- "get_next_dbg_record",
- "get_previous_dbg_record",
- "get_target_from_name",
- "get_target_from_triple",
- "intrinsic_get_type",
- "lookup_enum_attribute_kind",
- "replace_md_node_operand_with",
- "run_passes",
- "strip_module_debug_info",
- ]
- for name in removed_globals:
- assert not hasattr(llvm, name), name
-
- removed_class_methods = {
- llvm.Context: [
- "get_diagnostics",
- "create_enum_attribute",
- "create_string_attribute",
- "create_type_attribute",
- ],
- llvm.Module: ["get_verification_error"],
- llvm.Function: [
- "get_personality_fn",
- "set_personality_fn",
- "get_gc",
- "set_gc",
- "get_attribute_count",
- "get_enum_attribute",
- "add_attribute",
- "get_attributes",
- "get_string_attribute",
- "remove_enum_attribute",
- "remove_string_attribute",
- ],
- llvm.Attribute: ["kind"],
- llvm.AttributeAccessor: ["get_enum", "remove_enum"],
- llvm.Value: [
- "set_constant",
- "set_comdat",
- "set_thread_local",
- "set_externally_initialized",
- "set_volatile",
- "set_inst_alignment",
- "set_global_ifunc_resolver",
- "set_personality_fn",
- "set_prefix_data",
- "set_prologue_data",
- "set_nsw",
- "set_nuw",
- "set_exact",
- "set_nneg",
- "set_is_disjoint",
- "set_icmp_same_sign",
- "set_ordering",
- "set_atomic_sync_scope_id",
- "set_weak",
- "set_tail_call_kind",
- "set_called_operand",
- "set_cleanup",
- "set_fast_math_flags",
- "get_callsite_attribute_count",
- "get_callsite_enum_attribute",
- "add_callsite_attribute",
- ],
- }
- for cls, names in removed_class_methods.items():
- for name in names:
- assert not hasattr(cls, name), f"{cls.__name__}.{name}"
-
- assert isinstance(llvm.default_target_triple, str)
- assert isinstance(llvm.host_cpu_name, str)
- assert isinstance(llvm.host_cpu_features, str)
- assert isinstance(llvm.debug_metadata_version, int)
-
- with llvm.create_context() as ctx:
- i32 = ctx.types.i32
- i64 = ctx.types.i64
- ptr = ctx.types.ptr
- fn_ty = ctx.types.function(i32, [])
-
- assert i64.constant([1]).const_zext_value == 1
- assert i32.vector_const([i32.constant(1), i32.constant(2)]).is_constant
- assert i32.constant(1).get_cast_opcode(False, i64, False) == llvm.Opcode.ZExt
- assert ctx.get_intrinsic_type(llvm.lookup_intrinsic_id("llvm.memcpy"), [ptr, ptr, i64]).kind == llvm.TypeKind.Function
- asm_ty = ctx.types.function(ctx.types.void, [])
- assert asm_ty.inline_asm("nop", "", False, False, llvm.InlineAsmDialect.ATT, False).is_inline_asm
-
- with ctx.create_module("surface") as mod:
- fn = mod.add_function("f", fn_ty)
- bb = fn.append_basic_block("entry")
- assert bb.block_address().is_constant
-
- with mod.create_builder(bb) as builder:
- assert builder.function == fn
- assert builder.module == mod
- assert builder.context is not None
- ret = builder.ret(i32.constant(0))
- expect_raises(lambda: ret.has_dbg_records, "LLVM-C has no safe")
-
- with mod.create_builder(ret) as before_ret:
- assert before_ret.insert_block == bb
-
- assert not hasattr(mod, "get_or_insert_named_metadata")
- named_md = mod.add_named_metadata("llvm.nanobind.surface")
- assert mod.add_named_metadata("llvm.nanobind.surface") == named_md
-
- assert not hasattr(mod, "get_or_insert_comdat")
- comdat = mod.add_comdat("surface_comdat")
- comdat.selection_kind = llvm.ComdatSelectionKind.ExactMatch
- assert (
- mod.add_comdat("surface_comdat").selection_kind
- == llvm.ComdatSelectionKind.ExactMatch
- )
-
- opts = llvm.PassBuilderOptions()
- mod.run_passes("default", options=opts)
-
- bitcode = mod.write_bitcode_to_memory_buffer()
- with llvm.BinaryManager.from_bytes(bitcode) as binary:
- assert binary.copy_to_memory_buffer()
-
- target = llvm.Target.from_triple(llvm.default_target_triple)
- assert target is not None
- tm = llvm.TargetMachine.create(target, llvm.default_target_triple)
- td = tm.create_data_layout()
- assert str(llvm.TargetData.create(str(td))) == str(td)
-
- dis = llvm.Disasm.create(llvm.default_target_triple)
- assert hasattr(dis, "is_valid")
-
-
-if __name__ == "__main__":
- test_module_add_reuse_existing_semantics()
- test_named_struct_reuse_existing_semantics()
- test_member_factories_and_builder_navigation()
- print("test_api_surface_cleanup: PASSED")
diff --git a/tests/regressions/test_api_ux_cleanup.py b/tests/regressions/test_api_ux_cleanup.py
new file mode 100644
index 0000000..80d2438
--- /dev/null
+++ b/tests/regressions/test_api_ux_cleanup.py
@@ -0,0 +1,397 @@
+"""
+Regression tests for the API UX cleanup helpers.
+
+These tests cover the high-level APIs from devdocs/api-ux-cleanup:
+- Builder.intrinsic(...)
+- Module.optimize(...)
+- Function.optimize(...)
+- TargetMachine.host()
+- Module.emit_object()/emit_assembly()
+- JIT through LLVM-C ORC LLJIT
+"""
+
+import ctypes
+
+import llvm
+
+
+def _simple_i32_module(name: str = "simple"):
+ ctx_mgr = llvm.create_context()
+ ctx = ctx_mgr.__enter__()
+ mod_mgr = ctx.create_module(name)
+ mod = mod_mgr.__enter__()
+
+ i32 = ctx.types.i32
+ fn = mod.add_function("answer", ctx.types.function(i32, []))
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.ret(builder.add(i32.constant(40), i32.constant(2), "sum"))
+
+ return ctx_mgr, mod_mgr, ctx, mod
+
+
+def _close_module(ctx_mgr, mod_mgr):
+ try:
+ mod_mgr.__exit__(None, None, None)
+ finally:
+ ctx_mgr.__exit__(None, None, None)
+
+
+def _host_target_machine_or_skip():
+ try:
+ return llvm.TargetMachine.host()
+ except llvm.LLVMError as exc:
+ print(f"host target unavailable, skipping target-dependent check: {exc}")
+ return None
+
+
+def _host_jit_or_skip():
+ try:
+ return llvm.JIT.host()
+ except llvm.LLVMError as exc:
+ print(f"host JIT unavailable, skipping JIT check: {exc}")
+ return None
+
+
+def test_builder_intrinsic_non_overloaded():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("intrinsic_trap") as mod:
+ fn = mod.add_function("f", ctx.types.function(ctx.types.void, []))
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.intrinsic("llvm.trap", [])
+ builder.unreachable()
+
+ ir = str(mod)
+ assert "call void @llvm.trap()" in ir
+ assert mod.verify(), mod.verification_error
+
+
+def test_builder_intrinsic_overloaded_float():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("intrinsic_sqrt") as mod:
+ f64 = ctx.types.f64
+ fn = mod.add_function("mysqrt", ctx.types.function(f64, [f64]))
+ x = fn.get_param(0)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ y = builder.intrinsic(
+ "llvm.sqrt", [x], overloaded_types=[f64], name_hint="y"
+ )
+ builder.ret(y)
+
+ ir = str(mod)
+ assert "@llvm.sqrt.f64" in ir
+ assert mod.verify(), mod.verification_error
+
+
+def test_builder_intrinsic_memcpy():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("intrinsic_memcpy") as mod:
+ void = ctx.types.void
+ ptr = ctx.types.ptr
+ i64 = ctx.types.i64
+ i1 = ctx.types.i1
+ fn = mod.add_function("copy", ctx.types.function(void, [ptr, ptr, i64]))
+ dst = fn.get_param(0)
+ src = fn.get_param(1)
+ n = fn.get_param(2)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.intrinsic(
+ "llvm.memcpy",
+ [dst, src, n, i1.constant(False)],
+ overloaded_types=[ptr, ptr, i64],
+ )
+ builder.ret_void()
+
+ ir = str(mod)
+ assert "@llvm.memcpy" in ir
+ assert mod.verify(), mod.verification_error
+
+
+def test_builder_intrinsic_errors_are_clear():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("intrinsic_errors") as mod:
+ f64 = ctx.types.f64
+ fn = mod.add_function("f", ctx.types.function(f64, [f64]))
+ x = fn.get_param(0)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ try:
+ builder.intrinsic("llvm.not_a_real_intrinsic", [])
+ except llvm.LLVMAssertionError as exc:
+ assert "Unknown LLVM intrinsic" in str(exc)
+ else:
+ raise AssertionError("expected unknown intrinsic error")
+
+ try:
+ builder.intrinsic("llvm.sqrt", [x])
+ except llvm.LLVMAssertionError as exc:
+ assert "overloaded_types" in str(exc)
+ else:
+ raise AssertionError("expected overloaded_types error")
+
+ builder.ret(x)
+
+
+def test_module_optimize_success_and_failure():
+ ctx_mgr, mod_mgr, _ctx, mod = _simple_i32_module("optimize_helper")
+ try:
+ assert mod.verify(), mod.verification_error
+ mod.optimize("default")
+ assert mod.verify(), mod.verification_error
+
+ try:
+ mod.optimize("not-a-real-pass")
+ except llvm.LLVMError as exc:
+ message = str(exc)
+ assert "not-a-real-pass" in message
+ assert "Failed to optimize" in message
+ else:
+ raise AssertionError("expected invalid pipeline error")
+ finally:
+ _close_module(ctx_mgr, mod_mgr)
+
+
+def test_function_optimize_success_and_failure():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("function_optimize_helper") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("answer", ctx.types.function(i32, [i32]))
+ x = fn.get_param(0)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ result = builder.add(x, i32.constant(0), "result")
+ builder.ret(result)
+
+ untouched = mod.add_function("untouched", ctx.types.function(i32, [i32]))
+ y = untouched.get_param(0)
+ untouched_entry = untouched.append_basic_block("entry")
+ with untouched_entry.create_builder() as builder:
+ result = builder.add(y, i32.constant(0), "result")
+ builder.ret(result)
+
+ decl = mod.add_function("decl", ctx.types.function(i32, []))
+
+ assert mod.verify(), mod.verification_error
+ fn.optimize("instcombine,simplifycfg")
+ assert mod.verify(), mod.verification_error
+
+ ir = str(mod)
+ optimized_body = ir.split("define i32 @answer", 1)[1].split(
+ "define i32 @untouched", 1
+ )[0]
+ untouched_body = ir.split("define i32 @untouched", 1)[1].split(
+ "declare i32 @decl", 1
+ )[0]
+ assert "ret i32 %0" in optimized_body
+ assert " add i32 " not in optimized_body
+ assert " add i32 " in untouched_body
+
+ try:
+ fn.optimize("not-a-real-function-pass")
+ except llvm.LLVMError as exc:
+ message = str(exc)
+ assert "not-a-real-function-pass" in message
+ assert "Failed to optimize function" in message
+ else:
+ raise AssertionError("expected invalid function pipeline error")
+
+ try:
+ decl.optimize("instcombine")
+ except llvm.LLVMAssertionError as exc:
+ assert "function definition" in str(exc)
+ else:
+ raise AssertionError("expected declaration optimization error")
+
+
+def test_module_optimize_with_target_machine():
+ tm = _host_target_machine_or_skip()
+ if tm is None:
+ return
+
+ ctx_mgr, mod_mgr, _ctx, mod = _simple_i32_module("optimize_with_tm")
+ try:
+ mod.optimize("default", target_machine=tm)
+ assert mod.verify(), mod.verification_error
+ finally:
+ _close_module(ctx_mgr, mod_mgr)
+
+
+def test_emit_object_and_assembly_convenience():
+ tm = _host_target_machine_or_skip()
+ if tm is None:
+ return
+
+ ctx_mgr, mod_mgr, _ctx, mod = _simple_i32_module("emit_helper")
+ try:
+ mod.optimize("default", target_machine=tm)
+
+ obj = mod.emit_object(target_machine=tm)
+ asm = mod.emit_assembly(target_machine=tm)
+ obj_host = mod.emit_object()
+ asm_host = mod.emit_assembly()
+
+ assert isinstance(obj, bytes) and len(obj) > 0
+ assert isinstance(asm, bytes) and len(asm) > 0
+ assert isinstance(obj_host, bytes) and len(obj_host) > 0
+ assert isinstance(asm_host, bytes) and len(asm_host) > 0
+
+ with llvm.BinaryManager.from_bytes(obj) as binary:
+ assert binary.type in {
+ llvm.BinaryType.COFF,
+ llvm.BinaryType.ELF32L,
+ llvm.BinaryType.ELF32B,
+ llvm.BinaryType.ELF64L,
+ llvm.BinaryType.ELF64B,
+ llvm.BinaryType.MachO32L,
+ llvm.BinaryType.MachO32B,
+ llvm.BinaryType.MachO64L,
+ llvm.BinaryType.MachO64B,
+ llvm.BinaryType.Wasm,
+ }
+ finally:
+ _close_module(ctx_mgr, mod_mgr)
+
+
+def test_jit_integer_function_and_module_transfer():
+ jit = _host_jit_or_skip()
+ if jit is None:
+ return
+
+ with jit:
+ with llvm.create_context() as ctx:
+ with ctx.create_module("jit_add") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("add_i32", ctx.types.function(i32, [i32, i32]))
+ a = fn.get_param(0)
+ b = fn.get_param(1)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.ret(builder.add(a, b, "sum"))
+
+ assert mod.verify(), mod.verification_error
+ jit.add_module(mod)
+
+ try:
+ _ = mod.name
+ except llvm.LLVMMemoryError as exc:
+ assert "disposed" in str(exc)
+ else:
+ raise AssertionError("expected module to be invalid after JIT transfer")
+
+ address = jit.lookup("add_i32")
+ assert isinstance(address, int) and address != 0
+
+ add_i32 = jit.ctypes_function(
+ "add_i32", ctypes.c_int32, [ctypes.c_int32, ctypes.c_int32]
+ )
+ assert add_i32(2, 3) == 5
+
+
+def test_jit_missing_symbol_error():
+ jit = _host_jit_or_skip()
+ if jit is None:
+ return
+
+ with jit:
+ try:
+ jit.lookup("missing_symbol")
+ except llvm.LLVMError as exc:
+ assert "missing_symbol" in str(exc)
+ else:
+ raise AssertionError("expected missing symbol lookup to fail")
+
+
+def test_jit_ctypes_function_keeps_jit_alive():
+ jit = _host_jit_or_skip()
+ if jit is None:
+ return
+
+ with llvm.create_context() as ctx:
+ with ctx.create_module("jit_keepalive") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("add_i32_keepalive", ctx.types.function(i32, [i32, i32]))
+ a = fn.get_param(0)
+ b = fn.get_param(1)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ builder.ret(builder.add(a, b, "sum"))
+
+ jit.add_module(mod)
+ add_i32 = jit.ctypes_function(
+ "add_i32_keepalive", ctypes.c_int32, [ctypes.c_int32, ctypes.c_int32]
+ )
+
+ del jit
+ assert add_i32(4, 6) == 10
+
+
+def test_jit_callback_symbol():
+ jit = _host_jit_or_skip()
+ if jit is None:
+ return
+
+ callback_type = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_int32)
+
+ @callback_type
+ def py_inc(x):
+ return x + 10
+
+ with jit:
+ jit.add_symbol("py_inc", py_inc)
+
+ with llvm.create_context() as ctx:
+ with ctx.create_module("jit_callback") as mod:
+ i32 = ctx.types.i32
+ callee = mod.add_function("py_inc", ctx.types.function(i32, [i32]))
+ fn = mod.add_function("call_py_inc", ctx.types.function(i32, [i32]))
+ x = fn.get_param(0)
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ result = builder.call(callee, [x], "result")
+ builder.ret(result)
+
+ assert mod.verify(), mod.verification_error
+ jit.add_module(mod)
+
+ call_py_inc = jit.ctypes_function(
+ "call_py_inc", ctypes.c_int32, [ctypes.c_int32]
+ )
+ assert call_py_inc(5) == 15
+
+
+if __name__ == "__main__":
+ test_builder_intrinsic_non_overloaded()
+ print("test_builder_intrinsic_non_overloaded: PASSED")
+
+ test_builder_intrinsic_overloaded_float()
+ print("test_builder_intrinsic_overloaded_float: PASSED")
+
+ test_builder_intrinsic_memcpy()
+ print("test_builder_intrinsic_memcpy: PASSED")
+
+ test_builder_intrinsic_errors_are_clear()
+ print("test_builder_intrinsic_errors_are_clear: PASSED")
+
+ test_module_optimize_success_and_failure()
+ print("test_module_optimize_success_and_failure: PASSED")
+
+ test_module_optimize_with_target_machine()
+ print("test_module_optimize_with_target_machine: PASSED")
+
+ test_emit_object_and_assembly_convenience()
+ print("test_emit_object_and_assembly_convenience: PASSED")
+
+ test_jit_integer_function_and_module_transfer()
+ print("test_jit_integer_function_and_module_transfer: PASSED")
+
+ test_jit_missing_symbol_error()
+ print("test_jit_missing_symbol_error: PASSED")
+
+ test_jit_ctypes_function_keeps_jit_alive()
+ print("test_jit_ctypes_function_keeps_jit_alive: PASSED")
+
+ test_jit_callback_symbol()
+ print("test_jit_callback_symbol: PASSED")
diff --git a/tests/regressions/test_attribute_kind_lookup.py b/tests/regressions/test_attribute_kind_lookup.py
index 3960148..87e0773 100644
--- a/tests/regressions/test_attribute_kind_lookup.py
+++ b/tests/regressions/test_attribute_kind_lookup.py
@@ -74,6 +74,59 @@ def test_attribute_factories_and_function_accessors():
assert mod.verify(), mod.verification_error
+def test_memory_attribute_helpers():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("memory_attrs") as mod:
+ void = ctx.types.void
+ fn_ty = ctx.types.function(void, [])
+ fn = mod.add_function("f", fn_ty)
+
+ memory_none = llvm.Attribute.memory(ctx, "none")
+ assert memory_none.value == 0
+ fn.attributes.add(memory_none)
+ memory_attr = fn.attributes.get("memory")
+ assert memory_attr is not None
+ assert memory_attr.value == 0
+ assert "memory(none)" in str(mod)
+
+ fn.attributes.remove("memory")
+ fn.attributes.add_memory("read")
+ memory_attr = fn.attributes.get("memory")
+ assert memory_attr is not None
+ assert memory_attr.value == llvm.Attribute.memory(ctx, "read").value
+ assert "memory(read)" in str(mod)
+
+ fn.attributes.remove("memory")
+ fn.attributes.add_memory("argmem: read")
+ memory_attr = fn.attributes.get("memory")
+ assert memory_attr is not None
+ assert memory_attr.value == 1
+ assert "memory(argmem: read)" in str(mod)
+
+ fn.attributes.remove("memory")
+ complex_effects = (
+ "memory(argmem: read, inaccessiblemem: write, other: readwrite)"
+ )
+ fn.attributes.add_memory(complex_effects)
+ memory_attr = fn.attributes.get("memory")
+ assert memory_attr is not None
+ assert (
+ memory_attr.value == llvm.Attribute.memory(ctx, complex_effects).value
+ )
+ ir = str(mod)
+ assert "argmem: read" in ir
+ assert "inaccessiblemem: write" in ir
+
+ try:
+ fn.attributes.add_memory("argmem: frobnicate")
+ except llvm.LLVMAssertionError as exc:
+ assert "Unknown memory access effect" in str(exc)
+ else:
+ raise AssertionError("expected invalid memory access error")
+
+ assert mod.verify(), mod.verification_error
+
+
def test_callsite_attribute_accessors():
with llvm.create_context() as ctx:
with ctx.create_module("callsite_attr_accessors") as mod:
@@ -123,6 +176,9 @@ if __name__ == "__main__":
test_attribute_factories_and_function_accessors()
print("test_attribute_factories_and_function_accessors: PASSED")
+ test_memory_attribute_helpers()
+ print("test_memory_attribute_helpers: PASSED")
+
test_callsite_attribute_accessors()
print("test_callsite_attribute_accessors: PASSED")
diff --git a/tests/regressions/test_binary_iterator_end_guards.py b/tests/regressions/test_binary_iterator_end_guards.py
index ab2ccb6..3d09d52 100644
--- a/tests/regressions/test_binary_iterator_end_guards.py
+++ b/tests/regressions/test_binary_iterator_end_guards.py
@@ -5,6 +5,7 @@ Accessing properties on end iterators used to hard-abort in LLVM APIs.
Bindings should raise LLVMAssertionError instead.
"""
+import gc
from pathlib import Path
import llvm
@@ -61,6 +62,23 @@ def test_section_symbol_and_relocation_end_guards() -> None:
)
+def test_relocation_iterator_keeps_temporary_section_alive() -> None:
+ if not OBJECT_PATH.exists():
+ print(f"SKIP: object file not found: {OBJECT_PATH.resolve()}")
+ return
+
+ with llvm.BinaryManager.from_file(OBJECT_PATH) as binary:
+ probe = binary.sections
+ if probe.is_at_end():
+ print("SKIP: object file has no sections")
+ return
+
+ reloc = binary.sections.relocations
+ gc.collect()
+
+ assert isinstance(reloc.is_at_end(), bool)
+
+
def test_contains_symbol_and_move_to_containing_section_require_non_end_symbol() -> None:
if not OBJECT_PATH.exists():
print(f"SKIP: object file not found: {OBJECT_PATH.resolve()}")
@@ -85,5 +103,6 @@ def test_contains_symbol_and_move_to_containing_section_require_non_end_symbol()
if __name__ == "__main__":
test_section_symbol_and_relocation_end_guards()
+ test_relocation_iterator_keeps_temporary_section_alive()
test_contains_symbol_and_move_to_containing_section_require_non_end_symbol()
print("test_binary_iterator_end_guards: PASSED")
diff --git a/tests/regressions/test_binary_iterators.py b/tests/regressions/test_binary_iterators.py
index 4953204..9159652 100644
--- a/tests/regressions/test_binary_iterators.py
+++ b/tests/regressions/test_binary_iterators.py
@@ -29,6 +29,22 @@ def _manual_symbol_names(binary: llvm.Binary) -> list[str]:
return names
+def _assert_exhausted_next_is_stable(iterator) -> None:
+ while True:
+ try:
+ next(iterator)
+ except StopIteration:
+ break
+
+ for _ in range(2):
+ try:
+ next(iterator)
+ except StopIteration:
+ pass
+ else:
+ raise AssertionError("expected exhausted iterator to stay exhausted")
+
+
def test_binary_iterators_match_manual_iteration():
obj = Path("llvm-c/llvm-c-test/inputs/simple.o")
if not obj.exists():
@@ -52,6 +68,23 @@ def test_binary_iterators_match_manual_iteration():
)
+def test_binary_iterators_stay_stopped_after_exhaustion():
+ obj = Path("llvm-c/llvm-c-test/inputs/simple.o")
+ if not obj.exists():
+ print(f"SKIP: object file not found: {obj.resolve()}")
+ return
+
+ with llvm.BinaryManager.from_file(obj) as binary:
+ _assert_exhausted_next_is_stable(iter(binary.sections))
+ _assert_exhausted_next_is_stable(iter(binary.symbols))
+
+ sections = binary.sections
+ if not sections.is_at_end():
+ _assert_exhausted_next_is_stable(iter(sections.relocations))
+
+
if __name__ == "__main__":
test_binary_iterators_match_manual_iteration()
print("test_binary_iterators_match_manual_iteration: PASSED")
+ test_binary_iterators_stay_stopped_after_exhaustion()
+ print("test_binary_iterators_stay_stopped_after_exhaustion: PASSED")
diff --git a/tests/regressions/test_full_tree_guard_matrix.py b/tests/regressions/test_full_tree_guard_matrix.py
index 29e2d8d..84a56bb 100644
--- a/tests/regressions/test_full_tree_guard_matrix.py
+++ b/tests/regressions/test_full_tree_guard_matrix.py
@@ -4,8 +4,8 @@ Full-tree regression matrix for remaining wrapper guard paths.
This complements value/type/non-value matrices with:
- helper API assertions (phi/switch/indirectbr, call builders, vector_const)
- manager state-machine guards (Context/Module/Builder/DIBuilder/Binary)
-- wrapper lifetime guards (TypeFactory/Attribute/Comdat/NamedMDNode/Use/
- ValueMetadataEntries)
+- wrapper lifetime guards (TypeFactory/Attribute/Comdat/NamedMetadataList/Use/
+ MetadataMap)
- attribute/operand-bundle/indexed metadata guards
- disassembler invalid-context guards
"""
@@ -166,15 +166,16 @@ def test_attribute_operand_bundle_and_entries_guards():
add_inst = b.add(fn.get_param(0), i32.constant(1, False), "sum")
b.ret(add_inst)
- md = ctx.md_string("meta")
- kind = ctx.get_md_kind_id("llvm_nanobind.meta")
- add_inst.set_metadata(kind, md, ctx)
- entries = add_inst.instruction_get_all_metadata_other_than_debug_loc()
- assert len(entries) >= 1
- _ = entries.get_kind(0)
- _ = entries.get_metadata(0)
- assert_out_of_range(lambda: entries.get_kind(len(entries)))
- assert_out_of_range(lambda: entries.get_metadata(len(entries)))
+ md = ctx.md_node([ctx.md_string("meta")])
+ add_inst.metadata["llvm_nanobind.meta"] = md
+ assert "llvm_nanobind.meta" in add_inst.metadata
+ assert add_inst.metadata["llvm_nanobind.meta"] == md
+ assert add_inst.metadata.get("missing.kind") is None
+ try:
+ _ = add_inst.metadata["missing.kind"]
+ assert False, "Expected KeyError for missing metadata"
+ except KeyError:
+ pass
def test_manager_state_machine_guards():
@@ -223,7 +224,7 @@ def test_manager_state_machine_guards():
dm2 = mod.create_dibuilder()
dib = dm2.__enter__()
- _ = dib.create_file("x.c", ".")
+ _ = dib.file("x.c", ".")
assert_memory_error(
lambda: dm2.dispose(),
"cannot call dispose() after __enter__",
@@ -283,7 +284,7 @@ def test_lifetime_guards_for_remaining_wrappers():
escaped["types"] = ctx.types
escaped["attr"] = llvm.Attribute.string(ctx, "k", "v")
escaped["comdat"] = mod.add_comdat("C")
- escaped["named_md"] = mod.add_named_metadata("llvm.nanobind.named")
+ escaped["named_md"] = mod.named_metadata["llvm.nanobind.named"]
fn_ty = ctx.types.function(i32, [i32], False)
fn = mod.add_function("f", fn_ty)
@@ -296,20 +297,19 @@ def test_lifetime_guards_for_remaining_wrappers():
assert len(uses) >= 1
escaped["use"] = uses[0]
- md = ctx.md_string("meta")
- kind = ctx.get_md_kind_id("llvm_nanobind.lifetime")
- add_inst.set_metadata(kind, md, ctx)
- escaped["entries"] = add_inst.instruction_get_all_metadata_other_than_debug_loc()
- assert len(escaped["entries"]) >= 1
+ md = ctx.md_node([ctx.md_string("meta")])
+ add_inst.metadata["llvm_nanobind.lifetime"] = md
+ escaped["metadata"] = add_inst.metadata
+ assert escaped["metadata"].get("llvm_nanobind.lifetime") == md
assert_memory_error(lambda: escaped["types"].i32, "typefactory used after context")
assert_memory_error(lambda: escaped["attr"].string_kind, "attribute used after context")
assert_memory_error(lambda: escaped["comdat"].selection_kind, "comdat used after module")
- assert_memory_error(lambda: escaped["named_md"].name, "namedmdnode used after context")
+ assert_memory_error(lambda: len(escaped["named_md"]), "named metadata view used after context")
assert_memory_error(lambda: escaped["use"].user, "use used after context")
assert_memory_error(
- lambda: escaped["entries"].get_kind(0),
- "valuemetadataentries used after context",
+ lambda: escaped["metadata"].get("llvm_nanobind.lifetime"),
+ "metadata view used after context",
)
diff --git a/tests/regressions/test_github_issues_11_12_13.py b/tests/regressions/test_github_issues_11_12_13.py
new file mode 100644
index 0000000..7f39bb3
--- /dev/null
+++ b/tests/regressions/test_github_issues_11_12_13.py
@@ -0,0 +1,65 @@
+"""Regression coverage for GitHub issues 11, 12, and 13."""
+
+import llvm
+
+
+def test_issue_11_types_aliases_share_context_type_factory() -> None:
+ with llvm.create_context() as ctx:
+ with ctx.create_module("types_aliases") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("f", ctx.types.function(i32, [i32]))
+ bb = fn.append_basic_block("entry")
+ with bb.create_builder() as b:
+ value = b.add(fn.get_param(0), i32.constant(1), "value")
+ b.ret(value)
+
+ assert ctx.types == mod.types
+ assert ctx.types == fn.types
+ assert ctx.types == bb.types
+ assert ctx.types == value.types
+ assert mod.types.i64 == ctx.types.i64
+ assert fn.types.function(i32, [i32]) == ctx.types.function(i32, [i32])
+
+
+def test_issue_12_function_create_builder_creates_entry_block() -> None:
+ with llvm.create_context() as ctx:
+ with ctx.create_module("function_create_builder") as mod:
+ i64 = mod.types.i64
+ fn = mod.add_function("lift2", mod.types.function(i64, [i64, i64]))
+
+ with fn.create_builder() as ir:
+ ir.ret(i64.constant(0))
+
+ assert fn.basic_block_count == 1
+ assert fn.entry_block.name == "entry"
+ assert mod.verify(), mod.verification_error
+ assert "define i64 @lift2" in str(mod)
+ assert "ret i64 0" in str(mod)
+
+
+def test_issue_13_alloca_count_overload_replaces_array_alloca() -> None:
+ with llvm.create_context() as ctx:
+ with ctx.create_module("alloca_count_overload") as mod:
+ void = mod.types.void
+ i32 = mod.types.i32
+ fn = mod.add_function("f", mod.types.function(void, []))
+
+ with fn.create_builder() as b:
+ scalar = b.alloca(i32, "scalar")
+ array = b.alloca(i32, i32.constant(4), "array")
+ b.ret_void()
+
+ assert not hasattr(llvm.Builder, "array_alloca")
+ assert not scalar.is_array_allocation
+ assert array.is_array_allocation
+ ir = str(mod)
+ assert "%scalar = alloca i32" in ir
+ assert "%array = alloca i32, i32 4" in ir
+ assert mod.verify(), mod.verification_error
+
+
+if __name__ == "__main__":
+ test_issue_11_types_aliases_share_context_type_factory()
+ test_issue_12_function_create_builder_creates_entry_block()
+ test_issue_13_alloca_count_overload_replaces_array_alloca()
+ print("test_github_issues_11_12_13: PASSED")
diff --git a/tests/regressions/test_memory_module_lifetimes.py b/tests/regressions/test_memory_module_lifetimes.py
new file mode 100644
index 0000000..d432f09
--- /dev/null
+++ b/tests/regressions/test_memory_module_lifetimes.py
@@ -0,0 +1,168 @@
+"""
+Regression tests for module-owned wrapper lifetime guards.
+
+Root cause: many wrappers kept only an LLVM context token while holding raw
+references to values, blocks, uses, operand bundles, metadata views, or borrowed
+modules owned by an LLVMModuleRef. Disposing the module while the context stayed
+alive left those raw references dangling. Each wrapper must reject use through a
+Python LLVMMemoryError before calling LLVM-C on freed module-owned storage.
+"""
+
+from pathlib import Path
+
+import llvm
+
+
+def expect_memory_error(action, expected: str):
+ try:
+ action()
+ except llvm.LLVMMemoryError as exc:
+ assert expected.lower() in str(exc).lower(), str(exc)
+ else:
+ raise AssertionError("expected LLVMMemoryError")
+
+
+def expect_assertion(action, expected: str):
+ try:
+ action()
+ except llvm.LLVMAssertionError as exc:
+ assert expected.lower() in str(exc).lower(), str(exc)
+ else:
+ raise AssertionError("expected LLVMAssertionError")
+
+
+def test_module_owned_wrappers_reject_use_after_module_disposed():
+ ctx_mgr = llvm.create_context()
+ ctx = ctx_mgr.__enter__()
+ try:
+ mod_mgr = ctx.create_module("module_lifetimes")
+ mod = mod_mgr.__enter__()
+
+ i32 = ctx.types.i32
+ fn = mod.add_function("f", ctx.types.function(i32, [i32]))
+ borrowed_mod = fn.module
+ param = fn.get_param(0)
+ attr_view = fn.attributes
+ bb = fn.append_basic_block("entry")
+ bb_value = bb.as_value()
+ builder_manager = bb.create_builder()
+ operand_bundle = llvm.create_operand_bundle("deopt", [param], ctx)
+
+ with bb.create_builder() as builder:
+ inst = builder.add(param, i32.constant(1), "sum")
+ builder.ret(inst)
+
+ uses = param.uses
+ assert uses
+ use = uses[0]
+ metadata_view = inst.metadata
+ value_metadata = param.as_metadata()
+ with mod.create_dibuilder() as dib:
+ di_file = dib.file("x.c", ".")
+
+ mod_mgr.__exit__(None, None, None)
+
+ expect_memory_error(lambda: fn.name, "module was disposed")
+ expect_memory_error(lambda: param.name, "module was disposed")
+ expect_memory_error(lambda: bb.name, "module was disposed")
+ expect_memory_error(lambda: bb_value.name, "module was disposed")
+ expect_memory_error(lambda: inst.name, "module was disposed")
+ expect_memory_error(lambda: use.user, "module was disposed")
+ expect_memory_error(lambda: attr_view.get("noreturn"), "module was disposed")
+ expect_memory_error(lambda: borrowed_mod.name, "module has been disposed")
+ expect_memory_error(lambda: builder_manager.__enter__(), "module has been disposed")
+ expect_memory_error(lambda: operand_bundle.num_args, "module was disposed")
+ expect_memory_error(lambda: metadata_view.get("dbg"), "module was disposed")
+ expect_memory_error(lambda: value_metadata.kind, "module was disposed")
+ expect_memory_error(lambda: di_file.kind, "module was disposed")
+ finally:
+ ctx_mgr.__exit__(None, None, None)
+
+
+def test_context_manager_results_reject_use_after_exit():
+ ctx_mgr = llvm.create_context()
+ ctx = ctx_mgr.__enter__()
+ ctx_mgr.__exit__(None, None, None)
+ expect_memory_error(lambda: ctx.types.i32, "context")
+
+ with llvm.create_context() as live_ctx:
+ mod_mgr = live_ctx.create_module("escaped_module")
+ mod = mod_mgr.__enter__()
+ mod_mgr.__exit__(None, None, None)
+ expect_memory_error(lambda: mod.name, "module")
+
+ mod_mgr = live_ctx.create_module("escaped_builder")
+ mod = mod_mgr.__enter__()
+ fn = mod.add_function("f", live_ctx.types.function(live_ctx.types.void, []))
+ bb = fn.append_basic_block("entry")
+ builder_mgr = bb.create_builder()
+ builder = builder_mgr.__enter__()
+ builder_mgr.__exit__(None, None, None)
+ expect_memory_error(lambda: builder.ret_void(), "builder")
+
+ dib_mgr = mod.create_dibuilder()
+ dib = dib_mgr.__enter__()
+ dib_mgr.__exit__(None, None, None)
+ expect_memory_error(lambda: dib.file("x.c", "."), "dibuilder")
+ mod_mgr.__exit__(None, None, None)
+
+
+def test_binary_manager_result_rejects_use_after_exit():
+ bitcode_path = Path(__file__).parent / "factorial.bc"
+ binary_mgr = llvm.BinaryManager.from_bytes(bitcode_path.read_bytes())
+ binary = binary_mgr.__enter__()
+ binary_mgr.__exit__(None, None, None)
+ expect_memory_error(lambda: binary.type, "binary")
+
+
+def test_builder_debug_location_rejects_cross_context_metadata():
+ with llvm.create_context() as ctx1:
+ with llvm.create_context() as ctx2:
+ with ctx1.create_module("debug_location_context_mismatch") as mod:
+ fn = mod.add_function("f", ctx1.types.function(ctx1.types.void, []))
+ bb = fn.append_basic_block("entry")
+ local_scope = ctx1.md_node([])
+ foreign_scope = ctx2.md_node([])
+ foreign_inlined_at = ctx2.md_node([])
+
+ with bb.create_builder() as builder:
+ expect_assertion(
+ lambda: builder.debug_location(
+ line=1, column=1, scope=foreign_scope
+ ),
+ "same context",
+ )
+ expect_assertion(
+ lambda: builder.debug_location(
+ line=1,
+ column=1,
+ scope=local_scope,
+ inlined_at=foreign_inlined_at,
+ ),
+ "same context",
+ )
+ expect_assertion(
+ lambda: builder.debug_location(foreign_scope),
+ "same context",
+ )
+ expect_assertion(
+ lambda: ctx1.debug_location(
+ line=1, column=1, scope=foreign_scope
+ ),
+ "same context",
+ )
+ builder.ret_void()
+
+
+if __name__ == "__main__":
+ test_module_owned_wrappers_reject_use_after_module_disposed()
+ print("test_module_owned_wrappers_reject_use_after_module_disposed: PASSED")
+
+ test_context_manager_results_reject_use_after_exit()
+ print("test_context_manager_results_reject_use_after_exit: PASSED")
+
+ test_binary_manager_result_rejects_use_after_exit()
+ print("test_binary_manager_result_rejects_use_after_exit: PASSED")
+
+ test_builder_debug_location_rejects_cross_context_metadata()
+ print("test_builder_debug_location_rejects_cross_context_metadata: PASSED")
diff --git a/tests/regressions/test_metadata_ux_cleanup.py b/tests/regressions/test_metadata_ux_cleanup.py
new file mode 100644
index 0000000..b82d3a4
--- /dev/null
+++ b/tests/regressions/test_metadata_ux_cleanup.py
@@ -0,0 +1,351 @@
+"""
+Regression tests for the Pythonic metadata/debug-info API.
+
+These cover:
+- Value.metadata mapping view
+- Module.named_metadata mapping/list view
+- Module.module_flags view
+- Builder.debug_location(...) context manager
+- DIBuilder convenience recipes for files, compile units, functions, and locals
+"""
+
+import llvm
+
+
+def test_value_metadata_mapping_set_get_delete():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("metadata_mapping") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("f", ctx.types.function(i32, [i32]))
+ entry = fn.append_basic_block("entry")
+
+ with entry.create_builder() as builder:
+ inst = builder.add(fn.get_param(0), i32.constant(1), "sum")
+ marker = ctx.md_node([ctx.md_string("example.metadata")])
+
+ inst.metadata["example.kind"] = marker
+ assert "example.kind" in inst.metadata
+ assert inst.metadata.get("example.kind") == marker
+ assert inst.metadata["example.kind"] == marker
+
+ try:
+ _ = inst.metadata["missing.kind"]
+ except KeyError:
+ pass
+ else:
+ raise AssertionError("expected KeyError for missing metadata")
+
+ builder.ret(inst)
+
+ ir_with_metadata = str(mod)
+ assert "!example.kind" in ir_with_metadata
+
+ del inst.metadata["example.kind"]
+ assert inst.metadata.get("example.kind") is None
+ assert "!example.kind" not in str(mod)
+ assert mod.verify(), mod.verification_error
+
+
+def test_named_metadata_view_append_iterate_and_get():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("named_metadata_view") as mod:
+ node = ctx.md_node([ctx.md_string("named")])
+
+ assert "custom.named" not in mod.named_metadata
+ assert mod.named_metadata.get("custom.named") is None
+
+ mod.named_metadata["custom.named"].append(node)
+
+ assert "custom.named" in mod.named_metadata
+ named = mod.named_metadata["custom.named"]
+ assert len(named) == 1
+ assert named[0] == node
+ assert list(named) == [node]
+ assert mod.named_metadata.get("custom.named") is not None
+ assert "custom.named" in mod.named_metadata.keys()
+ assert "custom.named" in list(mod.named_metadata)
+ assert "!custom.named = !{" in str(mod)
+
+
+def test_md_string_value_round_trips_to_python_string():
+ with llvm.create_context() as ctx:
+ md_string = ctx.md_string("hello metadata")
+ assert isinstance(md_string.kind, int)
+ assert md_string.is_string
+ assert not md_string.is_node
+ assert not md_string.is_value
+ assert md_string.string == "hello metadata"
+
+ md_node = ctx.md_node([md_string])
+ assert isinstance(md_node.kind, int)
+ assert not md_node.is_string
+ assert md_node.is_node
+ assert not md_node.is_value
+ assert len(md_node.operands) == 1
+ assert md_node.operands[0].string == "hello metadata"
+
+ try:
+ _ = md_node.string
+ except llvm.LLVMAssertionError as exc:
+ assert "not an MDString" in str(exc)
+ else:
+ raise AssertionError("expected non-MDString string to fail")
+
+ try:
+ _ = md_string.operands
+ except llvm.LLVMAssertionError as exc:
+ assert "not an MDNode" in str(exc)
+ else:
+ raise AssertionError("expected non-MDNode operands to fail")
+
+ value_md = ctx.types.i32.constant(7).as_metadata()
+ assert value_md.is_value
+ try:
+ _ = value_md.value
+ except NotImplementedError as exc:
+ assert "ValueAsMetadata" in str(exc)
+ else:
+ raise AssertionError("expected Metadata.value to be unimplemented")
+
+
+def test_metadata_attachment_rejects_cross_context_metadata():
+ with llvm.create_context() as ctx1:
+ with llvm.create_context() as ctx2:
+ with ctx1.create_module("metadata_context_mismatch") as mod:
+ i32 = ctx1.types.i32
+ fn = mod.add_function("f", ctx1.types.function(i32, [i32]))
+ entry = fn.append_basic_block("entry")
+
+ with entry.create_builder() as builder:
+ inst = builder.add(fn.get_param(0), i32.constant(1), "sum")
+ foreign = ctx2.md_node([ctx2.md_string("foreign")])
+
+ try:
+ inst.metadata["example.foreign"] = foreign
+ except llvm.LLVMAssertionError as exc:
+ assert "same context" in str(exc)
+ else:
+ raise AssertionError("expected cross-context metadata error")
+
+ builder.ret(inst)
+
+ assert "example.foreign" not in str(mod)
+ assert mod.verify(), mod.verification_error
+
+
+def test_metadata_map_rejects_use_after_module_disposed():
+ ctx_mgr = llvm.create_context()
+ ctx = ctx_mgr.__enter__()
+ try:
+ mod_mgr = ctx.create_module("metadata_map_lifetime")
+ mod = mod_mgr.__enter__()
+ fn = mod.add_function("f", ctx.types.function(ctx.types.void, []))
+ metadata_view = fn.metadata
+
+ mod_mgr.__exit__(None, None, None)
+
+ try:
+ metadata_view.get("example.kind")
+ except llvm.LLVMMemoryError as exc:
+ assert "module was disposed" in str(exc)
+ else:
+ raise AssertionError("expected disposed-module metadata view error")
+ finally:
+ ctx_mgr.__exit__(None, None, None)
+
+
+def test_metadata_copy_to_replaces_raw_kind_id_copying():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("metadata_copy") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("f", ctx.types.function(i32, [i32]))
+ entry = fn.append_basic_block("entry")
+
+ with entry.create_builder() as builder:
+ first = builder.add(fn.get_param(0), i32.constant(1), "first")
+ second = builder.add(first, i32.constant(2), "second")
+ marker = ctx.md_node([ctx.md_string("copied")])
+ first.metadata["example.copy"] = marker
+
+ first.metadata.copy_to(second)
+ builder.ret(second)
+
+ assert second.metadata.get("example.copy") == marker
+ assert str(mod).count("!example.copy") == 2
+ assert mod.verify(), mod.verification_error
+
+
+def test_metadata_mapping_works_on_detached_instruction():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("detached_metadata") as mod:
+ fn = mod.add_function("f", ctx.types.function(ctx.types.void, []))
+ entry = fn.append_basic_block("entry")
+
+ with entry.create_builder() as builder:
+ ret = builder.ret_void()
+
+ ret.remove_from_parent()
+ marker = ctx.md_node([ctx.md_string("detached")])
+ ret.metadata["example.detached"] = marker
+ assert ret.metadata["example.detached"] == marker
+ ret.delete_instruction()
+
+
+def test_module_flags_view_add_get_and_iterate_keys():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("module_flags_view") as mod:
+ version = ctx.types.i32.constant(3).as_metadata()
+
+ mod.module_flags.add(
+ "Debug Info Version", llvm.ModuleFlagBehavior.Warning, version
+ )
+
+ assert "Debug Info Version" in mod.module_flags
+ assert mod.module_flags.get("Debug Info Version") == version
+ assert mod.module_flags["Debug Info Version"] == version
+ assert "Debug Info Version" in mod.module_flags.keys()
+ assert "Debug Info Version" in list(mod.module_flags)
+ assert "!llvm.module.flags" in str(mod)
+
+
+def test_debug_location_manager_rejects_disposed_builder():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("debug_location_lifetime") as mod:
+ i32 = ctx.types.i32
+ fn = mod.add_function("f", ctx.types.function(i32, []))
+ entry = fn.append_basic_block("entry")
+ loc = ctx.md_node([])
+
+ with entry.create_builder() as builder:
+ manager = builder.debug_location(loc)
+ builder.ret(i32.constant(0))
+
+ try:
+ manager.__enter__()
+ except llvm.LLVMMemoryError as exc:
+ assert "builder was disposed" in str(exc)
+ else:
+ raise AssertionError("expected disposed-builder debug location error")
+
+
+def test_debug_location_context_manager_and_dibuilder_recipes():
+ with llvm.create_context() as ctx:
+ with ctx.create_module("debug_location_context") as mod:
+ mod.is_new_dbg_info_format = True
+ i32 = ctx.types.i32
+ fn = mod.add_function("add", ctx.types.function(i32, [i32, i32]))
+
+ with mod.create_dibuilder() as dib:
+ file = dib.file("main.c", ".")
+ compile_unit = dib.compile_unit(
+ language=llvm.DwarfLanguage.C,
+ file=file,
+ producer="llvm-nanobind-test",
+ )
+ subprogram = dib.function(
+ fn,
+ name="add",
+ file=file,
+ line=1,
+ return_type=i32,
+ param_types=[i32, i32],
+ )
+ local = dib.local_variable(subprogram, "tmp", file, 2, i32)
+
+ assert compile_unit is not None
+ assert subprogram is not None
+ assert local is not None
+
+ entry = fn.append_basic_block("entry")
+ with entry.create_builder() as builder:
+ with builder.debug_location(line=2, column=5, scope=subprogram):
+ sum_inst = builder.add(fn.get_param(0), fn.get_param(1), "sum")
+
+ loc = ctx.debug_location(line=3, column=7, scope=subprogram)
+ with builder.debug_location(loc):
+ result = builder.add(sum_inst, i32.constant(0), "result")
+
+ builder.ret(result)
+
+ dib.finalize()
+
+ ir = str(mod)
+ assert "define i32 @add" in ir
+ assert "!dbg" in ir
+ assert "line: 2, column: 5" in ir
+ assert "line: 3, column: 7" in ir
+ assert "!llvm.dbg.cu" in ir
+ assert mod.verify(), mod.verification_error
+
+
+def test_redundant_low_level_metadata_apis_are_removed():
+ assert not hasattr(llvm, "get_md_kind_id")
+ assert not hasattr(llvm, "ValueMetadataEntries")
+ assert not hasattr(llvm, "NamedMDNode")
+
+ assert not hasattr(llvm.Context, "get_md_kind_id")
+ assert not hasattr(llvm.Context, "create_debug_location")
+
+ assert not hasattr(llvm.Value, "set_metadata")
+ assert not hasattr(llvm.Value, "global_copy_all_metadata")
+ assert not hasattr(llvm.Value, "instruction_get_all_metadata_other_than_debug_loc")
+
+ assert not hasattr(llvm.Metadata, "as_value")
+ assert not hasattr(llvm.Metadata, "string_value")
+ assert not hasattr(llvm.Metadata, "__len__")
+ assert not hasattr(llvm.Metadata, "__getitem__")
+ assert not hasattr(llvm.Metadata, "__iter__")
+
+ assert not hasattr(llvm.Module, "first_named_metadata")
+ assert not hasattr(llvm.Module, "last_named_metadata")
+ assert not hasattr(llvm.Module, "get_named_metadata")
+ assert not hasattr(llvm.Module, "add_named_metadata")
+ assert not hasattr(llvm.Module, "get_named_metadata_num_operands")
+ assert not hasattr(llvm.Module, "get_named_metadata_operands")
+ assert not hasattr(llvm.Module, "add_named_metadata_operand")
+ assert not hasattr(llvm.Module, "add_module_flag")
+ assert not hasattr(llvm.Module, "get_module_flag")
+
+ assert not hasattr(llvm.DIBuilder, "create_file")
+ assert not hasattr(llvm.DIBuilder, "create_compile_unit")
+
+ # Advanced DIBuilder methods remain public because the convenience recipes do
+ # not cover custom debug types, manual subroutine types, or record insertion.
+ assert hasattr(llvm.DIBuilder, "create_function")
+ assert hasattr(llvm.DIBuilder, "create_auto_variable")
+ assert hasattr(llvm.DIBuilder, "create_struct_type")
+
+
+if __name__ == "__main__":
+ test_value_metadata_mapping_set_get_delete()
+ print("test_value_metadata_mapping_set_get_delete: PASSED")
+
+ test_named_metadata_view_append_iterate_and_get()
+ print("test_named_metadata_view_append_iterate_and_get: PASSED")
+
+ test_md_string_value_round_trips_to_python_string()
+ print("test_md_string_value_round_trips_to_python_string: PASSED")
+
+ test_metadata_attachment_rejects_cross_context_metadata()
+ print("test_metadata_attachment_rejects_cross_context_metadata: PASSED")
+
+ test_metadata_map_rejects_use_after_module_disposed()
+ print("test_metadata_map_rejects_use_after_module_disposed: PASSED")
+
+ test_metadata_copy_to_replaces_raw_kind_id_copying()
+ print("test_metadata_copy_to_replaces_raw_kind_id_copying: PASSED")
+
+ test_metadata_mapping_works_on_detached_instruction()
+ print("test_metadata_mapping_works_on_detached_instruction: PASSED")
+
+ test_module_flags_view_add_get_and_iterate_keys()
+ print("test_module_flags_view_add_get_and_iterate_keys: PASSED")
+
+ test_debug_location_manager_rejects_disposed_builder()
+ print("test_debug_location_manager_rejects_disposed_builder: PASSED")
+
+ test_debug_location_context_manager_and_dibuilder_recipes()
+ print("test_debug_location_context_manager_and_dibuilder_recipes: PASSED")
+
+ test_redundant_low_level_metadata_apis_are_removed()
+ print("test_redundant_low_level_metadata_apis_are_removed: PASSED")
diff --git a/tests/regressions/test_ollvm_obf_golden_master.py b/tests/regressions/test_ollvm_obf_golden_master.py
index ac3397d..66c041f 100644
--- a/tests/regressions/test_ollvm_obf_golden_master.py
+++ b/tests/regressions/test_ollvm_obf_golden_master.py
@@ -28,12 +28,6 @@ FIXTURES_DIR = Path("tests/golden/ollvm_obf")
@lru_cache(maxsize=1)
def target_machine() -> llvm.TargetMachine:
- llvm.initialize_all_target_infos()
- llvm.initialize_all_targets()
- llvm.initialize_all_target_mcs()
- llvm.initialize_all_asm_printers()
- llvm.initialize_all_asm_parsers()
-
triple = llvm.default_target_triple
target = llvm.Target.from_triple(triple)
assert target is not None
diff --git a/tests/regressions/test_transformation_gap_apis.py b/tests/regressions/test_transformation_gap_apis.py
index 84e5807..32143a2 100644
--- a/tests/regressions/test_transformation_gap_apis.py
+++ b/tests/regressions/test_transformation_gap_apis.py
@@ -12,7 +12,7 @@ def test_alloca_array_size_and_is_array_allocation() -> None:
with entry.create_builder() as b:
scalar = b.alloca(i32, "scalar")
- array = b.array_alloca(i32, i32.constant(4, False), "array")
+ array = b.alloca(i32, i32.constant(4, False), "array")
b.ret_void()
assert scalar.allocated_type == i32
diff --git a/tests/regressions/test_value_kind_guards.py b/tests/regressions/test_value_kind_guards.py
index 10da3d4..a5590ab 100644
--- a/tests/regressions/test_value_kind_guards.py
+++ b/tests/regressions/test_value_kind_guards.py
@@ -423,11 +423,6 @@ def test_value_accessor_guard_matrix_negative():
lambda: setattr(bad, "unnamed_address", g0.unnamed_address),
"global value",
),
- (
- "global_copy_all_metadata",
- lambda: bad.global_copy_all_metadata(),
- "global value",
- ),
(
"has_personality_fn",
lambda: bad.has_personality_fn,
@@ -457,11 +452,6 @@ def test_value_accessor_guard_matrix_negative():
lambda: setattr(bad, "prologue_data", ctx.types.ptr.null()),
"function value",
),
- (
- "instruction_get_all_metadata_other_than_debug_loc",
- lambda: bad.instruction_get_all_metadata_other_than_debug_loc(),
- "instruction value",
- ),
("opcode", lambda: bad.opcode, "instruction value"),
("opcode_name", lambda: bad.opcode_name, "instruction value"),
("next_instruction", lambda: bad.next_instruction, "instruction value"),
@@ -905,7 +895,7 @@ def test_value_accessor_guard_matrix_positive():
assert g0.global_value_type.kind == llvm.TypeKind.Integer
ua = g0.unnamed_address
g0.unnamed_address = ua
- assert len(g0.global_copy_all_metadata()) >= 0
+ g0.metadata.copy_to(g0)
# Function value.
assert not f.has_personality_fn
@@ -920,7 +910,7 @@ def test_value_accessor_guard_matrix_positive():
assert f.prologue_data is not None
# Instruction value.
- assert len(add_inst.instruction_get_all_metadata_other_than_debug_loc()) >= 0
+ add_inst.metadata.copy_to(add_inst)
assert add_inst.opcode == llvm.Opcode.Add
assert add_inst.opcode_name == "add"
next_inst = add_inst.next_instruction
diff --git a/tests/test_builder_memory.py b/tests/test_builder_memory.py
index aea0d49..4da276b 100755
--- a/tests/test_builder_memory.py
+++ b/tests/test_builder_memory.py
@@ -39,7 +39,7 @@ def main():
# Array alloca (dynamic size)
array_size = i32.constant(5)
- array_alloca = builder.array_alloca(i32, array_size, "dynamic_array")
+ array_alloca = builder.alloca(i32, array_size, "dynamic_array")
# Static array alloca
static_array = builder.alloca(arr_ty, "static_array")
diff --git a/tests/test_examples.py b/tests/test_examples.py
index 1392da9..7347b2a 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -88,3 +88,83 @@ def test_transform_replace_add_leaves_no_wrap_adds_alone() -> None:
"""
)
assert "%sum = add nsw i32 %x, %y" in output
+
+
+def test_intrinsic_memcpy_example() -> None:
+ output = run_example("examples/intrinsic_memcpy.py")
+ assert "define void @copy_bytes" in output
+ assert "call void @llvm.memcpy.p0.p0.i64" in output
+
+
+def test_optimize_module_example() -> None:
+ output = run_example("examples/optimize_module.py")
+ assert "define i32 @add_then_simplify" in output
+ assert "ret i32 %x" in output
+ assert "alloca" not in output
+
+
+def test_optimize_function_example() -> None:
+ output = run_example("examples/optimize_function.py")
+ assert "define i32 @optimize_me" in output
+ assert "define i32 @leave_me_alone" in output
+ assert "ret i32 %x" in output
+ assert "%result = add i32 %x, 0" in output
+ optimized = output.split("define i32 @optimize_me", 1)[1].split(
+ "define i32 @leave_me_alone", 1
+ )[0]
+ assert "alloca" not in optimized
+ assert " add i32 " not in optimized
+
+
+def test_emit_object_assembly_example() -> None:
+ output = run_example("examples/emit_object_assembly.py")
+ if output.startswith("skipped:"):
+ assert "host code generation is unavailable" in output
+ return
+ assert "target:" in output
+ assert "object bytes:" in output
+ assert "binary type:" in output
+ assert "assembly preview:" in output
+
+
+def test_jit_add_example() -> None:
+ output = run_example("examples/jit_add.py")
+ if output.startswith("skipped:"):
+ assert "host JIT is unavailable" in output
+ return
+ assert "add_i32(40, 2) = 42" in output
+ assert "call_python(5) = 15" in output
+
+
+def test_instruction_metadata_example() -> None:
+ output = run_example("examples/instruction_metadata.py")
+ assert "instruction metadata:" in output
+ assert "has example.note: True" in output
+ assert "round trip matches: True" in output
+ assert "note operands: 1" in output
+ assert "note text: created by instruction_metadata.py" in output
+ assert "define i32 @bump" in output
+ assert "%result = add i32" in output
+ assert "!example.note" in output
+ assert "created by instruction_metadata.py" in output
+
+
+def test_named_metadata_example() -> None:
+ output = run_example("examples/named_metadata.py")
+ assert "named metadata:" in output
+ assert "keys: example.tags" in output
+ assert "example.tags operands: 2" in output
+ assert "iterated operands: 2" in output
+ assert "first tag strings: frontend, demo" in output
+ assert "!example.tags = !{" in output
+ assert '"frontend"' in output
+ assert '"purpose"' in output
+
+
+def test_metadata_debug_info_example() -> None:
+ output = run_example("examples/metadata_debug_info.py")
+ assert "define i32 @add" in output
+ assert "!example.note" in output
+ assert "!example.compile_units" in output
+ assert "!llvm.module.flags" in output
+ assert "!dbg" in output
diff --git a/tests/test_feature_matrix.py b/tests/test_feature_matrix.py
index 03a952d..c80f907 100644
--- a/tests/test_feature_matrix.py
+++ b/tests/test_feature_matrix.py
@@ -66,22 +66,19 @@ def test_core_intrinsic_get_type():
print("[PASS] intrinsic_get_type works correctly")
-def test_core_replace_md_node_operand():
- """Test LLVMReplaceMDNodeOperandWith → Value.replace_md_node_operand_with()"""
+def test_core_metadata_node_operands():
+ """Test MDNode operand inspection through Metadata.operands."""
with llvm.create_context() as ctx:
- # Create metadata nodes
md1 = ctx.md_string("original")
md2 = ctx.md_string("replacement")
- md_node = ctx.md_node([md1])
+ md_node = ctx.md_node([md1, md2])
- # Convert to value to use with replace function
- md_val = md_node.as_value(ctx)
- replacement_val = md2.as_value(ctx)
+ assert [operand.string for operand in md_node.operands] == [
+ "original",
+ "replacement",
+ ]
- # Replace operand
- md_val.replace_md_node_operand_with(0, replacement_val.as_metadata())
-
- print("[PASS] replace_md_node_operand_with works correctly")
+ print("[PASS] metadata node operands work correctly")
def test_debuginfo_global_variable_expression():
@@ -90,16 +87,16 @@ def test_debuginfo_global_variable_expression():
with ctx.create_module("test") as mod:
with mod.create_dibuilder() as dib:
# Create necessary debug info
- file = dib.create_file("test.c", "/path")
- cu = dib.create_compile_unit(
- lang=DWARF_LANG_C,
+ file = dib.file("test.c", "/path")
+ cu = dib.compile_unit(
+ language=llvm.DwarfLanguage.C,
file=file,
producer="test",
is_optimized=False,
flags="",
runtime_ver=0,
split_name="",
- kind=llvm.DWARFEmissionFull,
+ kind=llvm.DwarfEmissionKind.Full,
dwo_id=0,
split_debug_inlining=True,
debug_info_for_profiling=False,
@@ -110,6 +107,12 @@ def test_debuginfo_global_variable_expression():
i32_ty = dib.create_basic_type(
"int", 32, DWARF_ATE_SIGNED, DI_FLAGS_ZERO
)
+ assert i32_ty.di_type_name == "int"
+ assert i32_ty.di_type_size_in_bits == 32
+ assert i32_ty.di_type_align_in_bits == 0
+ assert i32_ty.di_type_offset_in_bits == 0
+ assert i32_ty.di_type_line == 0
+ assert i32_ty.di_type_flags == DI_FLAGS_ZERO
# Create a global variable expression
gve = dib.create_global_variable_expression(
@@ -144,16 +147,16 @@ def test_debuginfo_class_type():
with llvm.create_context() as ctx:
with ctx.create_module("test") as mod:
with mod.create_dibuilder() as dib:
- file = dib.create_file("test.cpp", "/path")
- cu = dib.create_compile_unit(
- lang=DWARF_LANG_C_PLUS_PLUS,
+ file = dib.file("test.cpp", "/path")
+ cu = dib.compile_unit(
+ language=llvm.DwarfLanguage.CPlusPlus,
file=file,
producer="test",
is_optimized=False,
flags="",
runtime_ver=0,
split_name="",
- kind=llvm.DWARFEmissionFull,
+ kind=llvm.DwarfEmissionKind.Full,
dwo_id=0,
split_debug_inlining=True,
debug_info_for_profiling=False,
@@ -189,16 +192,16 @@ def test_debuginfo_static_member_type():
with llvm.create_context() as ctx:
with ctx.create_module("test") as mod:
with mod.create_dibuilder() as dib:
- file = dib.create_file("test.cpp", "/path")
- cu = dib.create_compile_unit(
- lang=DWARF_LANG_C_PLUS_PLUS,
+ file = dib.file("test.cpp", "/path")
+ cu = dib.compile_unit(
+ language=llvm.DwarfLanguage.CPlusPlus,
file=file,
producer="test",
is_optimized=False,
flags="",
runtime_ver=0,
split_name="",
- kind=llvm.DWARFEmissionFull,
+ kind=llvm.DwarfEmissionKind.Full,
dwo_id=0,
split_debug_inlining=True,
debug_info_for_profiling=False,
@@ -237,16 +240,16 @@ def test_debuginfo_member_pointer_type():
with llvm.create_context() as ctx:
with ctx.create_module("test") as mod:
with mod.create_dibuilder() as dib:
- file = dib.create_file("test.cpp", "/path")
- cu = dib.create_compile_unit(
- lang=DWARF_LANG_C_PLUS_PLUS,
+ file = dib.file("test.cpp", "/path")
+ cu = dib.compile_unit(
+ language=llvm.DwarfLanguage.CPlusPlus,
file=file,
producer="test",
is_optimized=False,
flags="",
runtime_ver=0,
split_name="",
- kind=llvm.DWARFEmissionFull,
+ kind=llvm.DwarfEmissionKind.Full,
dwo_id=0,
split_debug_inlining=True,
debug_info_for_profiling=False,
@@ -292,10 +295,6 @@ def test_debuginfo_member_pointer_type():
def test_object_binary_copy_to_memory_buffer():
"""Test LLVMBinaryCopyMemoryBuffer → binary.copy_to_memory_buffer()"""
- # Initialize targets first (before creating context)
- llvm.initialize_native_target()
- llvm.initialize_native_asm_printer()
-
with llvm.create_context() as ctx:
with ctx.create_module("test") as mod:
# Create a simple function
@@ -335,10 +334,6 @@ def test_object_binary_copy_to_memory_buffer():
def test_object_section_contains_symbol():
"""Test LLVMGetSectionContainsSymbol → section.contains_symbol()"""
- # Initialize targets first
- llvm.initialize_native_target()
- llvm.initialize_native_asm_printer()
-
with llvm.create_context() as ctx:
with ctx.create_module("test") as mod:
# Create a function
@@ -418,7 +413,7 @@ def test_all():
# Core.h
test_core_get_cast_opcode()
test_core_intrinsic_get_type()
- test_core_replace_md_node_operand()
+ test_core_metadata_node_operands()
# DebugInfo.h
test_debuginfo_global_variable_expression()
diff --git a/tests/test_passbuilder.py b/tests/test_passbuilder.py
index 34adc62..1ae01aa 100644
--- a/tests/test_passbuilder.py
+++ b/tests/test_passbuilder.py
@@ -98,13 +98,6 @@ def count_functions(mod):
def main():
- # Initialize all targets (needed for pass manager)
- llvm.initialize_all_target_infos()
- llvm.initialize_all_targets()
- llvm.initialize_all_target_mcs()
- llvm.initialize_all_asm_printers()
- llvm.initialize_all_asm_parsers()
-
# Get default target for running passes
triple = llvm.default_target_triple
target = llvm.Target.from_triple(triple)
diff --git a/tests/test_target_codegen.py b/tests/test_target_codegen.py
index d1b8665..d3016fd 100644
--- a/tests/test_target_codegen.py
+++ b/tests/test_target_codegen.py
@@ -7,9 +7,6 @@ This is the Python equivalent of tests/test_target_codegen.cpp.
Output should match the C++ golden master test.
LLVM Python APIs tested:
-- llvm.initialize_all_targets(), llvm.initialize_all_target_mcs()
-- llvm.initialize_all_asm_printers(), llvm.initialize_all_asm_parsers()
-- llvm.initialize_native_target(), llvm.initialize_native_asm_printer()
- llvm.default_target_triple, llvm.normalize_target_triple()
- llvm.host_cpu_name, llvm.host_cpu_features
- llvm.Target.from_triple(), llvm.Target.from_name()
@@ -26,13 +23,6 @@ import llvm
def main():
- # Initialize all targets
- llvm.initialize_all_target_infos()
- llvm.initialize_all_targets()
- llvm.initialize_all_target_mcs()
- llvm.initialize_all_asm_printers()
- llvm.initialize_all_asm_parsers()
-
print("; Test: test_target_codegen")
print("; Tests target initialization, queries, and code generation")
print(";")