diff --git a/README.md b/README.md
index 28b15f5..ac51873 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,9 @@ More examples worth browsing:
- [`examples/optimize_module.py`](examples/optimize_module.py) - optimize a module with a 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
@@ -70,7 +73,8 @@ 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
+- 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 initialization/lookup, data layouts, `Module.emit_object()`, `Module.emit_assembly()`, target-machine emission, and PassBuilder pipeline execution via `Module.optimize()` / `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
diff --git a/devdocs/api-design-philosophy.md b/devdocs/api-design-philosophy.md
index cb26da0..b50bb02 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
```
---
@@ -95,7 +95,7 @@ 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
Factories with a natural owner should be static methods instead (for example,
`BinaryManager.from_file()` rather than a module-level binary factory).
@@ -124,7 +124,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 2bdcd70..7418a4d 100644
--- a/devdocs/api-reference.md
+++ b/devdocs/api-reference.md
@@ -60,6 +60,26 @@ tm = llvm.TargetMachine.host()
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
@@ -96,7 +116,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`
@@ -118,7 +138,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`
diff --git a/devdocs/api-ux-cleanup/plan.md b/devdocs/api-ux-cleanup/plan.md
index 410941c..1e3e3fe 100644
--- a/devdocs/api-ux-cleanup/plan.md
+++ b/devdocs/api-ux-cleanup/plan.md
@@ -2,12 +2,13 @@
## Goal
-Make the remaining high-value workflows clean from a Python user's perspective while keeping useful LLVM controls available. This plan covers four areas only:
+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.
+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.
@@ -234,6 +235,50 @@ jit.add_symbol("py_func", callback)
- 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.
+- Removed raw APIs stay absent from the public surface.
+
## API audit process
After each phase:
@@ -251,6 +296,7 @@ After each phase:
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
@@ -258,6 +304,7 @@ After each phase:
- 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:
diff --git a/devdocs/api-ux-cleanup/progress.md b/devdocs/api-ux-cleanup/progress.md
index d2c2ddc..b4e0e99 100644
--- a/devdocs/api-ux-cleanup/progress.md
+++ b/devdocs/api-ux-cleanup/progress.md
@@ -7,7 +7,8 @@ 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.
+4. JIT through LLVM-C ORC LLJIT,
+5. metadata/debug-info cleanup.
## Implemented APIs
@@ -55,9 +56,27 @@ Implemented and tested the active UX work:
- [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:
@@ -92,6 +111,8 @@ Coverage:
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
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/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/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/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/src/llvm-nanobind.cpp b/src/llvm-nanobind.cpp
index 1b25f13..0ae44c6 100644
--- a/src/llvm-nanobind.cpp
+++ b/src/llvm-nanobind.cpp
@@ -255,6 +255,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;
@@ -3262,6 +3267,8 @@ struct LLVMValueWrapper {
// LLVMMetadataWrapper
LLVMMetadataWrapper as_metadata() const;
+ LLVMMetadataMapWrapper metadata() const;
+
// 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)
@@ -4791,6 +4798,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();
@@ -6696,6 +6709,9 @@ 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;
+
// =========================================================================
// Module API Refactor: Methods moved from global functions
// =========================================================================
@@ -7153,6 +7169,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);
@@ -9660,11 +9679,13 @@ struct LLVMMetadataWrapper;
struct LLVMDIBuilderWrapper : NoMoveCopy {
LLVMDIBuilderRef m_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
std::shared_ptr m_module_token;
LLVMDIBuilderWrapper(LLVMModuleRef mod,
std::shared_ptr module_token)
- : m_module_token(std::move(module_token)) {
+ : m_ctx_ref(mod ? LLVMGetModuleContext(mod) : nullptr),
+ m_module_token(std::move(module_token)) {
m_ref = LLVMCreateDIBuilder(mod);
}
@@ -9694,6 +9715,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,
@@ -9701,6 +9725,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,
@@ -9732,6 +9766,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
// =========================================================================
@@ -9829,6 +9873,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,
@@ -10055,11 +10108,13 @@ struct LLVMDIBuilderWrapper : NoMoveCopy {
struct LLVMMetadataWrapper {
LLVMMetadataRef m_ref = nullptr;
+ LLVMContextRef m_ctx_ref = nullptr;
std::shared_ptr m_context_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 token,
+ LLVMContextRef ctx = nullptr)
+ : m_ref(ref), m_ctx_ref(ctx), m_context_token(std::move(token)) {}
void check_valid() const {
if (!m_ref)
@@ -10068,6 +10123,77 @@ struct LLVMMetadataWrapper {
throw LLVMMemoryError("Metadata used after context was destroyed");
}
+ 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);
+ }
+ return result;
+ }
+
// Convert metadata to value (requires context)
LLVMValueWrapper as_value(LLVMContextWrapper &ctx) const {
check_valid();
@@ -10089,6 +10215,430 @@ struct LLVMMetadataWrapper {
}
};
+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 LLVMMetadataRef normalize_metadata_for_attachment(
+ LLVMContextRef ctx, const LLVMMetadataWrapper &md) {
+ md.check_valid();
+ 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;
+
+ LLVMMetadataMapWrapper() = default;
+ LLVMMetadataMapWrapper(LLVMValueRef value, LLVMContextRef ctx,
+ std::shared_ptr token)
+ : m_value_ref(value), m_ctx_ref(ctx), m_context_token(std::move(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 (!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);
+ }
+
+ 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);
+ }
+ }
+ 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);
+ 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();
+ 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);
+ }
+ 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 (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);
+ }
+
+ 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) {
@@ -10103,7 +10653,18 @@ 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);
+}
+
+inline LLVMMetadataMapWrapper LLVMValueWrapper::metadata() const {
+ check_valid();
+ LLVMContextRef ctx_ref = context_for_metadata_value(m_ref);
+ return LLVMMetadataMapWrapper(m_ref, ctx_ref, m_context_token);
}
// Implementation of replace_md_node_operand_with - needs LLVMMetadataWrapper
@@ -10155,7 +10716,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
@@ -10169,7 +10730,7 @@ LLVMContextWrapper::md_node(const Iterable &mds) {
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(
@@ -10185,7 +10746,37 @@ inline LLVMMetadataWrapper LLVMContextWrapper::create_debug_location(
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();
+ 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();
+ LLVMMetadataRef inlined = nullptr;
+ if (inlined_at) {
+ inlined_at->check_valid();
+ 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
@@ -10231,7 +10822,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);
+}
+
+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
@@ -10246,6 +10847,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,
@@ -10254,7 +10880,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_module_token, m_ctx_ref);
+}
+
+inline LLVMMetadataWrapper
+LLVMDIBuilderWrapper::file(const std::string &filename,
+ const std::string &directory) {
+ return create_file(filename, directory);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_compile_unit(
@@ -10273,7 +10905,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_module_token, m_ctx_ref);
+}
+
+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(
@@ -10287,7 +10933,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10299,7 +10945,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10311,7 +10957,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_module_token, m_ctx_ref);
}
// Function & Subroutine Creation
@@ -10330,7 +10976,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_subroutine_type(
@@ -10347,7 +10993,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_module_token, m_ctx_ref);
+}
+
+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_module_token, m_ctx_ref);
+ fn.set_subprogram(wrapper);
+ return wrapper;
}
// Type Creation
@@ -10359,7 +11042,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_pointer_type(
@@ -10371,7 +11054,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_vector_type(
@@ -10390,7 +11073,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_typedef(
@@ -10405,7 +11088,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_struct_type(
@@ -10433,7 +11116,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_enumeration_type(
@@ -10457,7 +11140,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_forward_decl(
@@ -10473,7 +11156,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10490,7 +11173,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_subrange_type(
@@ -10514,7 +11197,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_set_type(
@@ -10529,7 +11212,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_dynamic_array_type(
@@ -10560,7 +11243,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_module_token, m_ctx_ref);
}
// Variable & Expression
@@ -10576,7 +11259,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_auto_variable(
@@ -10592,7 +11275,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_module_token, m_ctx_ref);
+}
+
+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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10613,7 +11315,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10624,14 +11326,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_module_token, m_ctx_ref);
}
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_module_token, m_ctx_ref);
}
// Label Methods
@@ -10644,7 +11346,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_module_token, m_ctx_ref);
}
inline void
@@ -10707,7 +11409,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_array(
@@ -10721,7 +11423,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_array(
}
return LLVMMetadataWrapper(
LLVMDIBuilderGetOrCreateArray(m_ref, elem_refs.data(), elem_refs.size()),
- m_module_token);
+ m_module_token, m_ctx_ref);
}
// Enumerator Methods
@@ -10732,7 +11434,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10743,7 +11445,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_module_token, m_ctx_ref);
}
// ObjC & Inheritance Methods
@@ -10759,7 +11461,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_objc_ivar(
@@ -10776,7 +11478,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_inheritance(
@@ -10790,7 +11492,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_module_token, m_ctx_ref);
}
// Import & Macro Methods
@@ -10813,7 +11515,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10835,7 +11537,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_temp_macro_file(
@@ -10847,7 +11549,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_macro(
@@ -10860,7 +11562,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_macro(
(LLVMDWARFMacinfoRecordType)macro_type,
name.c_str(), name.size(), value.c_str(),
value.size()),
- m_module_token);
+ m_module_token, m_ctx_ref);
}
// Missing DIBuilder Method Implementations
@@ -10886,7 +11588,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_union_type(
@@ -10910,7 +11612,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_array_type(
@@ -10929,7 +11631,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10938,7 +11640,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10947,13 +11649,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_null_ptr_type() {
check_valid();
return LLVMMetadataWrapper(LLVMDIBuilderCreateNullPtrType(m_ref),
- m_module_token);
+ m_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_bit_field_member_type(
@@ -10970,7 +11672,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -10978,7 +11680,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_type_array(
@@ -10992,7 +11694,7 @@ inline LLVMMetadataWrapper LLVMDIBuilderWrapper::get_or_create_type_array(
}
return LLVMMetadataWrapper(LLVMDIBuilderGetOrCreateTypeArray(
m_ref, type_refs.data(), type_refs.size()),
- m_module_token);
+ m_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_lexical_block_file(
@@ -11003,7 +11705,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper LLVMDIBuilderWrapper::create_imported_declaration(
@@ -11024,7 +11726,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_module_token, m_ctx_ref);
}
inline LLVMMetadataWrapper
@@ -11038,7 +11740,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_module_token, m_ctx_ref);
}
// Utility Methods
@@ -11092,7 +11794,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_module_token, m_ctx_ref);
}
// Create static member type debug info
@@ -11113,7 +11815,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_module_token, m_ctx_ref);
}
// Create member pointer type debug info (C++ pointer-to-member)
@@ -11129,7 +11831,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_module_token, m_ctx_ref);
}
// Insert declare record before an instruction
@@ -11579,6 +12281,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)
@@ -12362,6 +13084,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,
@@ -12969,7 +13760,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.
@@ -13249,17 +14040,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.
@@ -13279,6 +14059,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.
@@ -13370,12 +14159,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,
@@ -13708,7 +14491,7 @@ Valid when:
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,
@@ -13720,7 +14503,7 @@ Valid when:
// =====================================================================
.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)")
@@ -13750,7 +14533,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)")
@@ -13813,6 +14596,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.
@@ -14494,42 +15301,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,
@@ -14703,40 +15474,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,
@@ -14751,7 +15488,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
@@ -14759,7 +15496,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.
@@ -14767,9 +15504,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
@@ -14795,40 +15532,25 @@ 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,
@@ -15176,10 +15898,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.
@@ -15203,8 +15921,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)");
@@ -15411,25 +16130,25 @@ initialize_all_asm_printers(), and initialize_all_asm_parsers().)");
// 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)");
@@ -15495,7 +16214,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,
@@ -15625,7 +16344,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
@@ -15636,11 +16355,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.
@@ -15674,7 +16393,7 @@ This initializes the native target and native ASM printer internally.
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.
@@ -15832,12 +16551,12 @@ objects are pinned by the JIT to keep callbacks alive.
.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.
@@ -15950,9 +16669,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.
@@ -16250,17 +16969,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
// ==========================================================================
@@ -16304,15 +17012,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,
@@ -16345,6 +17058,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,
@@ -16432,6 +17155,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,
@@ -16625,7 +17355,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.
@@ -16647,9 +17377,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.
@@ -16665,9 +17395,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.
@@ -16681,9 +17411,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.
@@ -16696,9 +17426,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.
@@ -16742,6 +17472,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; })
@@ -16751,10 +17499,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.
@@ -16765,6 +17509,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();
@@ -16772,14 +17550,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);
@@ -16795,7 +17603,7 @@ 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);
}, R"(Get scope from this debug location.
C API: LLVMDILocationGetScope)")
@@ -16804,7 +17612,7 @@ 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);
return std::nullopt;
}, R"(Get inlined-at location from this debug location, or None.
@@ -16814,7 +17622,7 @@ 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);
return std::nullopt;
}, R"(Get file from this debug scope, or None.
@@ -16854,7 +17662,7 @@ 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);
return std::nullopt;
}, R"(Get file from this debug variable, or None.
@@ -16864,7 +17672,7 @@ 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);
return std::nullopt;
}, R"(Get scope from this debug variable, or None.
@@ -16879,7 +17687,7 @@ Valid when:
self.check_valid();
return LLVMMetadataWrapper(
LLVMDIGlobalVariableExpressionGetVariable(self.m_ref),
- self.m_context_token);
+ self.m_context_token, self.m_ctx_ref);
}, R"(Get the DIGlobalVariable from this DIGlobalVariableExpression.
C API: LLVMDIGlobalVariableExpressionGetVariable)")
@@ -16887,7 +17695,7 @@ Valid when:
self.check_valid();
return LLVMMetadataWrapper(
LLVMDIGlobalVariableExpressionGetExpression(self.m_ref),
- self.m_context_token);
+ self.m_context_token, self.m_ctx_ref);
}, R"(Get the DIExpression from this DIGlobalVariableExpression.
C API: LLVMDIGlobalVariableExpressionGetExpression)");
@@ -16906,10 +17714,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).
@@ -16919,7 +17727,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)");
@@ -16938,7 +17746,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
index c580de0..8ef1fc2 100644
--- a/tests/regressions/test_api_surface_cleanup.py
+++ b/tests/regressions/test_api_surface_cleanup.py
@@ -158,6 +158,7 @@ def test_member_factories_and_builder_navigation() -> None:
"get_host_cpu_features",
"get_host_cpu_name",
"get_inline_asm",
+ "get_md_kind_id",
"get_di_node_tag",
"get_last_dbg_record",
"get_last_enum_attribute_kind",
@@ -171,6 +172,8 @@ def test_member_factories_and_builder_navigation() -> None:
"replace_md_node_operand_with",
"run_passes",
"strip_module_debug_info",
+ "NamedMDNode",
+ "ValueMetadataEntries",
]
for name in removed_globals:
assert not hasattr(llvm, name), name
@@ -178,11 +181,24 @@ def test_member_factories_and_builder_navigation() -> None:
removed_class_methods = {
llvm.Context: [
"get_diagnostics",
+ "get_md_kind_id",
+ "create_debug_location",
"create_enum_attribute",
"create_string_attribute",
"create_type_attribute",
],
- llvm.Module: ["get_verification_error"],
+ llvm.Module: [
+ "get_verification_error",
+ "first_named_metadata",
+ "last_named_metadata",
+ "get_named_metadata",
+ "add_named_metadata",
+ "get_named_metadata_num_operands",
+ "get_named_metadata_operands",
+ "add_named_metadata_operand",
+ "add_module_flag",
+ "get_module_flag",
+ ],
llvm.Function: [
"get_personality_fn",
"set_personality_fn",
@@ -198,6 +214,8 @@ def test_member_factories_and_builder_navigation() -> None:
],
llvm.Attribute: ["kind"],
llvm.AttributeAccessor: ["get_enum", "remove_enum"],
+ llvm.DIBuilder: ["create_file", "create_compile_unit"],
+ llvm.Metadata: ["as_value", "string_value", "__len__", "__getitem__", "__iter__"],
llvm.Value: [
"set_constant",
"set_comdat",
@@ -225,6 +243,9 @@ def test_member_factories_and_builder_navigation() -> None:
"get_callsite_attribute_count",
"get_callsite_enum_attribute",
"add_callsite_attribute",
+ "set_metadata",
+ "global_copy_all_metadata",
+ "instruction_get_all_metadata_other_than_debug_loc",
],
}
for cls, names in removed_class_methods.items():
@@ -265,8 +286,9 @@ def test_member_factories_and_builder_navigation() -> None:
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
+ named_md = mod.named_metadata["llvm.nanobind.surface"]
+ assert mod.named_metadata["llvm.nanobind.surface"] is not None
+ assert len(named_md) == 0
assert not hasattr(mod, "get_or_insert_comdat")
comdat = mod.add_comdat("surface_comdat")
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_metadata_ux_cleanup.py b/tests/regressions/test_metadata_ux_cleanup.py
new file mode 100644
index 0000000..2611f71
--- /dev/null
+++ b/tests/regressions/test_metadata_ux_cleanup.py
@@ -0,0 +1,299 @@
+"""
+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_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_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_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_examples.py b/tests/test_examples.py
index 065a523..73002e0 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -121,3 +121,37 @@ def test_jit_add_example() -> None:
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..f194325 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,
@@ -418,7 +421,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()