Add high-level API for (named) metadata

This commit is contained in:
Duncan Ogilvie
2026-05-13 12:41:10 +02:00
parent 2c832090ee
commit 723641ee50
18 changed files with 1813 additions and 437 deletions
+5 -1
View File
@@ -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
+14 -4
View File
@@ -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 `<sub>C API: ...</sub>` 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 `<sub>C API limitation</sub>`.
---
### 9. Method Chaining Potential
**Principle**: Methods returning values enable fluent APIs.
+22 -2
View File
@@ -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`
+49 -2
View File
@@ -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(<pass pipeline>)`.
- 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:
+22 -1
View File
@@ -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
+57
View File
@@ -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()
+77
View File
@@ -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()
+49
View File
@@ -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()
+27 -27
View File
@@ -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:
+18 -76
View File
@@ -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:
+11 -18
View File
@@ -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
+1050 -242
View File
File diff suppressed because it is too large Load Diff
+25 -3
View File
@@ -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")
@@ -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",
)
@@ -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")
+2 -12
View File
@@ -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
+34
View File
@@ -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
+31 -28
View File
@@ -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()