mirror of
https://github.com/LLVMParty/llvm-nanobind
synced 2026-06-21 13:43:38 +00:00
549 lines
19 KiB
Python
549 lines
19 KiB
Python
"""
|
|
llvm-c-test debug info command implementations.
|
|
|
|
This module implements debug info related commands like --get-di-tag,
|
|
--di-type-get-name, and --test-dibuilder.
|
|
"""
|
|
|
|
import llvm
|
|
|
|
|
|
def get_di_tag():
|
|
"""Test LLVMGetDINodeTag functionality.
|
|
|
|
This is a silent test that creates debug info nodes and queries their tags.
|
|
Returns 0 on success.
|
|
"""
|
|
# Create module with global context
|
|
ctx = llvm.global_context()
|
|
with ctx.create_module("Mod") as mod:
|
|
# Create metadata string and node
|
|
string_md = ctx.md_string("foo")
|
|
node_md = ctx.md_node([string_md])
|
|
|
|
# Get tag from node (should be 0 for generic node)
|
|
tag = node_md.di_node_tag
|
|
assert tag == 0, f"Expected tag 0, got {tag}"
|
|
|
|
# Create DIBuilder
|
|
with mod.create_dibuilder() as dib:
|
|
# Create file
|
|
file_md = dib.file("metadata.c", ".")
|
|
|
|
# Create struct type (DW_TAG_structure_type = 0x13)
|
|
struct_md = dib.create_struct_type(
|
|
file_md,
|
|
"TestClass",
|
|
file_md,
|
|
42, # line number
|
|
64, # size in bits
|
|
0, # align in bits
|
|
llvm.DIFlagObjcClassComplete, # flags
|
|
)
|
|
|
|
# Get tag from struct (should be 0x13 = DW_TAG_structure_type)
|
|
tag = struct_md.di_node_tag
|
|
assert tag == 0x13, f"Expected tag 0x13, got {tag:#x}"
|
|
|
|
return 0
|
|
|
|
|
|
def di_type_get_name():
|
|
"""Test LLVMDITypeGetName functionality.
|
|
|
|
This is a silent test that creates a debug info type and queries its name.
|
|
Returns 0 on success.
|
|
"""
|
|
# Create module with global context
|
|
ctx = llvm.global_context()
|
|
with ctx.create_module("Mod") as mod:
|
|
# Create DIBuilder
|
|
with mod.create_dibuilder() as dib:
|
|
# Create file
|
|
file_md = dib.file("metadata.c", ".")
|
|
|
|
# Create struct type with name
|
|
name = "TestClass"
|
|
struct_md = dib.create_struct_type(
|
|
file_md,
|
|
name,
|
|
file_md,
|
|
42, # line number
|
|
64, # size in bits
|
|
0, # align in bits
|
|
llvm.DIFlagObjcClassComplete, # flags
|
|
)
|
|
|
|
# Get name from type
|
|
type_name = struct_md.di_type_name
|
|
|
|
# Verify name matches
|
|
assert len(type_name) == len(name), (
|
|
f"Expected length {len(name)}, got {len(type_name)}"
|
|
)
|
|
assert type_name == name, f"Expected name '{name}', got '{type_name}'"
|
|
|
|
return 0
|
|
|
|
|
|
def declare_objc_class(dib, file_md):
|
|
"""Helper function to declare ObjC class with inheritance and property."""
|
|
# Create TestClass struct
|
|
decl = dib.create_struct_type(
|
|
file_md,
|
|
"TestClass",
|
|
file_md,
|
|
42, # line
|
|
64, # size in bits
|
|
0, # align
|
|
llvm.DIFlagObjcClassComplete,
|
|
)
|
|
|
|
# Create TestSuperClass struct
|
|
super_decl = dib.create_struct_type(
|
|
file_md,
|
|
"TestSuperClass",
|
|
file_md,
|
|
42, # line
|
|
64, # size in bits
|
|
0, # align
|
|
llvm.DIFlagObjcClassComplete,
|
|
)
|
|
|
|
# Create inheritance
|
|
dib.create_inheritance(decl, super_decl, 0, 0, 0)
|
|
|
|
# Create ObjC property
|
|
test_property = dib.create_objc_property(
|
|
"test",
|
|
file_md,
|
|
42,
|
|
"getTest",
|
|
"setTest",
|
|
0x20 | 0x40, # copy | nonatomic
|
|
super_decl,
|
|
)
|
|
|
|
# Create ObjC ivar
|
|
dib.create_objc_ivar(
|
|
"_test",
|
|
file_md,
|
|
42,
|
|
64, # size in bits
|
|
0, # align
|
|
64, # offset
|
|
llvm.DIFlagPublic,
|
|
super_decl,
|
|
test_property,
|
|
)
|
|
|
|
return decl
|
|
|
|
|
|
def test_dibuilder():
|
|
"""Test LLVMDIBuilder functionality.
|
|
|
|
Creates a comprehensive module with debug info and prints the IR.
|
|
Tests most DIBuilder APIs.
|
|
"""
|
|
filename = "debuginfo.c"
|
|
ctx = llvm.global_context()
|
|
|
|
with ctx.create_module(filename) as mod:
|
|
# Enable new debug info format
|
|
mod.is_new_dbg_info_format = True
|
|
assert mod.is_new_dbg_info_format
|
|
|
|
# Create DIBuilder
|
|
with mod.create_dibuilder() as dib:
|
|
# Create file
|
|
file_md = dib.file(filename, ".")
|
|
|
|
# Create compile unit
|
|
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
|
|
module_md = dib.create_module(
|
|
compile_unit, "llvm-c-test", "", "/test/include/llvm-c-test.h", ""
|
|
)
|
|
|
|
# Create another module for import testing
|
|
other_module = dib.create_module(
|
|
compile_unit,
|
|
"llvm-c-test-import",
|
|
"",
|
|
"/test/include/llvm-c-test-import.h",
|
|
"",
|
|
)
|
|
|
|
# Create imported module
|
|
imported_module = dib.create_imported_module_from_module(
|
|
module_md, other_module, file_md, 42, []
|
|
)
|
|
|
|
# Create imported from alias
|
|
dib.create_imported_module_from_alias(
|
|
module_md, imported_module, file_md, 42, []
|
|
)
|
|
|
|
# Create ObjC class
|
|
class_ty = declare_objc_class(dib, file_md)
|
|
|
|
# Create global class variable expression
|
|
global_class_value_expr = dib.create_constant_value_expression(0)
|
|
dib.create_global_variable_expression(
|
|
module_md,
|
|
"globalClass",
|
|
"",
|
|
file_md,
|
|
1,
|
|
class_ty,
|
|
True,
|
|
global_class_value_expr,
|
|
None,
|
|
0,
|
|
)
|
|
|
|
# Create Int64 basic type
|
|
int64_ty = dib.create_basic_type("Int64", 64, 0, llvm.DIFlagZero)
|
|
|
|
# Create typedef
|
|
int64_typedef = dib.create_typedef(
|
|
int64_ty, "int64_t", file_md, 42, file_md, 0
|
|
)
|
|
|
|
# Create global variable
|
|
global_var_expr = dib.create_constant_value_expression(0)
|
|
dib.create_global_variable_expression(
|
|
module_md,
|
|
"global",
|
|
"",
|
|
file_md,
|
|
1,
|
|
int64_typedef,
|
|
True,
|
|
global_var_expr,
|
|
None,
|
|
0,
|
|
)
|
|
|
|
# Create namespace
|
|
namespace = dib.create_namespace(module_md, "NameSpace", False)
|
|
|
|
# Create struct type with elements
|
|
struct_elts = [int64_ty, int64_ty, int64_ty]
|
|
struct_dbg_ty = dib.create_struct_type(
|
|
namespace,
|
|
"MyStruct",
|
|
file_md,
|
|
0,
|
|
192,
|
|
0,
|
|
0,
|
|
None, # derived_from
|
|
struct_elts, # elements
|
|
llvm.DWARFSourceLanguageC, # runtime_lang
|
|
None, # vtable_holder
|
|
"MyStruct", # unique_id
|
|
)
|
|
|
|
# Create pointer type
|
|
struct_dbg_ptr_ty = dib.create_pointer_type(struct_dbg_ty, 192, 0, 0, "")
|
|
|
|
# Add to named metadata
|
|
mod.named_metadata["FooType"].append(struct_dbg_ptr_ty)
|
|
|
|
# Create function
|
|
i64_type = ctx.types.i64
|
|
vec_type = i64_type.vector(10)
|
|
foo_param_tys = [i64_type, i64_type, vec_type]
|
|
foo_func_ty = ctx.types.function(i64_type, foo_param_tys, False)
|
|
foo_function = mod.add_function("foo", foo_func_ty)
|
|
foo_entry_block = foo_function.append_basic_block("entry")
|
|
|
|
# Create vector type metadata
|
|
subscripts = [dib.get_or_create_subrange(0, 10)]
|
|
vector_ty = dib.create_vector_type(64 * 10, 0, int64_ty, subscripts)
|
|
|
|
# Create subroutine type
|
|
param_types = [int64_ty, int64_ty, vector_ty]
|
|
function_ty = dib.create_subroutine_type(file_md, param_types, 0)
|
|
|
|
# Create replaceable function metadata
|
|
replaceable_function_md = dib.create_replaceable_composite_type(
|
|
0x15, "foo", file_md, file_md, 42, 0, 0, 0, llvm.DIFlagFwdDecl, ""
|
|
)
|
|
|
|
# Create debug location for parameters
|
|
foo_param_location = ctx.debug_location(
|
|
42, 0, replaceable_function_md, None
|
|
)
|
|
|
|
# Create function debug info
|
|
function_md = dib.create_function(
|
|
file_md, "foo", "foo", file_md, 42, None, True, True, 42, 0, False
|
|
)
|
|
|
|
# Replace temporary metadata
|
|
replaceable_function_md.replace_all_uses_with(function_md)
|
|
|
|
# Replace function type
|
|
function_md.replace_di_subprogram_type(function_ty)
|
|
|
|
# Create expression for parameters
|
|
foo_param_expr = dib.create_expression([])
|
|
|
|
# Create parameter variables and insert declare records
|
|
foo_param_var1 = dib.create_parameter_variable(
|
|
function_md, "a", 1, file_md, 42, int64_ty, True, 0
|
|
)
|
|
zero_val = i64_type.constant(0)
|
|
dib.insert_declare_record_at_end(
|
|
zero_val,
|
|
foo_param_var1,
|
|
foo_param_expr,
|
|
foo_param_location,
|
|
foo_entry_block,
|
|
)
|
|
|
|
foo_param_var2 = dib.create_parameter_variable(
|
|
function_md, "b", 2, file_md, 42, int64_ty, True, 0
|
|
)
|
|
dib.insert_declare_record_at_end(
|
|
zero_val,
|
|
foo_param_var2,
|
|
foo_param_expr,
|
|
foo_param_location,
|
|
foo_entry_block,
|
|
)
|
|
|
|
foo_param_var3 = dib.create_parameter_variable(
|
|
function_md, "c", 3, file_md, 42, vector_ty, True, 0
|
|
)
|
|
dib.insert_declare_record_at_end(
|
|
zero_val,
|
|
foo_param_var3,
|
|
foo_param_expr,
|
|
foo_param_location,
|
|
foo_entry_block,
|
|
)
|
|
|
|
# Set subprogram
|
|
foo_function.set_subprogram(function_md)
|
|
|
|
# Create label
|
|
foo_label1 = dib.create_label(function_md, "label1", file_md, 42, False)
|
|
dib.insert_label_at_end(foo_label1, foo_param_location, foo_entry_block)
|
|
|
|
# Create lexical block
|
|
foo_lexical_block = dib.create_lexical_block(function_md, file_md, 42, 0)
|
|
|
|
# Create another basic block for variables
|
|
foo_var_block = foo_function.append_basic_block("vars")
|
|
foo_vars_location = ctx.debug_location(
|
|
43, 0, function_md, None
|
|
)
|
|
|
|
# Create auto variables with debug value records
|
|
foo_var1 = dib.create_auto_variable(
|
|
foo_lexical_block, "d", file_md, 43, int64_ty, True, 0, 0
|
|
)
|
|
foo_val1 = i64_type.constant(0)
|
|
foo_var_value_expr1 = dib.create_constant_value_expression(0)
|
|
dib.insert_dbg_value_record_at_end(
|
|
foo_val1,
|
|
foo_var1,
|
|
foo_var_value_expr1,
|
|
foo_vars_location,
|
|
foo_var_block,
|
|
)
|
|
|
|
foo_var2 = dib.create_auto_variable(
|
|
foo_lexical_block, "e", file_md, 44, int64_ty, True, 0, 0
|
|
)
|
|
foo_val2 = i64_type.constant(1)
|
|
foo_var_value_expr2 = dib.create_constant_value_expression(1)
|
|
dib.insert_dbg_value_record_at_end(
|
|
foo_val2,
|
|
foo_var2,
|
|
foo_var_value_expr2,
|
|
foo_vars_location,
|
|
foo_var_block,
|
|
)
|
|
|
|
# Create macro file
|
|
macro_file = dib.create_temp_macro_file(None, 0, file_md)
|
|
dib.create_macro(
|
|
macro_file, 0, llvm.DWARFMacinfoRecordTypeDefine, "SIMPLE_DEFINE", ""
|
|
)
|
|
dib.create_macro(
|
|
macro_file, 0, llvm.DWARFMacinfoRecordTypeDefine, "VALUE_DEFINE", "1"
|
|
)
|
|
|
|
# Create enumerators
|
|
enumerator_test_a = dib.create_enumerator("Test_A", 0, True)
|
|
enumerator_test_b = dib.create_enumerator("Test_B", 1, True)
|
|
enumerator_test_c = dib.create_enumerator("Test_C", 2, True)
|
|
enumerators_test = [enumerator_test_a, enumerator_test_b, enumerator_test_c]
|
|
|
|
# Create enumeration type
|
|
enum_test = dib.create_enumeration_type(
|
|
namespace, "EnumTest", file_md, 0, 64, 0, enumerators_test, int64_ty
|
|
)
|
|
mod.named_metadata["EnumTest"].append(enum_test)
|
|
|
|
# Create UInt128 type and large enumerators
|
|
uint128_ty = dib.create_basic_type("UInt128", 128, 0, llvm.DIFlagZero)
|
|
words_test_d = [0x098A224000000000, 0x4B3B4CA85A86C47A]
|
|
words_test_e = [0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF]
|
|
|
|
large_enum_test_d = dib.create_enumerator_of_arbitrary_precision(
|
|
"Test_D", words_test_d, False
|
|
)
|
|
large_enum_test_e = dib.create_enumerator_of_arbitrary_precision(
|
|
"Test_E", words_test_e, False
|
|
)
|
|
large_enumerators = [large_enum_test_d, large_enum_test_e]
|
|
|
|
large_enum_test = dib.create_enumeration_type(
|
|
namespace,
|
|
"LargeEnumTest",
|
|
file_md,
|
|
0,
|
|
128,
|
|
0,
|
|
large_enumerators,
|
|
uint128_ty,
|
|
)
|
|
mod.named_metadata["LargeEnumTest"].append(large_enum_test)
|
|
|
|
# Create subrange type with metadata bounds
|
|
foo_val3 = i64_type.constant(8)
|
|
foo_val4 = i64_type.constant(4)
|
|
lo = foo_val1.as_metadata()
|
|
hi = foo_val2.as_metadata()
|
|
strd = foo_val3.as_metadata()
|
|
bias = foo_val4.as_metadata()
|
|
|
|
subrange_md_ty = dib.create_subrange_type(
|
|
file_md, "foo", 42, file_md, 64, 0, 0, int64_ty, lo, hi, strd, bias
|
|
)
|
|
mod.named_metadata["SubrangeType"].append(subrange_md_ty)
|
|
|
|
# Create set types
|
|
set_md_ty1 = dib.create_set_type(
|
|
file_md, "enumset", file_md, 42, 64, 0, enum_test
|
|
)
|
|
set_md_ty2 = dib.create_set_type(
|
|
file_md, "subrangeset", file_md, 42, 64, 0, subrange_md_ty
|
|
)
|
|
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)]
|
|
loc_expr = dib.create_expression([])
|
|
rank_expr = dib.create_expression([])
|
|
dynamic_array_md_ty = dib.create_dynamic_array_type(
|
|
file_md,
|
|
"foo",
|
|
42,
|
|
file_md,
|
|
64 * 10,
|
|
0,
|
|
int64_ty,
|
|
dyn_subscripts,
|
|
loc_expr,
|
|
foo_var1, # associated - matches C code which passes FooVar1
|
|
None,
|
|
rank_expr,
|
|
None,
|
|
)
|
|
mod.named_metadata["DynType"].append(dynamic_array_md_ty)
|
|
|
|
# Create forward declaration
|
|
struct_p_ty = dib.create_forward_decl(
|
|
2, "Class1", namespace, file_md, 0, 0, 192, 0, "FooClass"
|
|
)
|
|
|
|
# Create array and replace it
|
|
int32_ty = dib.create_basic_type("Int32", 32, 0, llvm.DIFlagZero)
|
|
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.named_metadata["ClassType"].append(struct_p_ty)
|
|
|
|
# Build IR instructions
|
|
with foo_entry_block.create_builder() as builder:
|
|
builder.br(foo_var_block)
|
|
|
|
# Build another br with label
|
|
foo_label2 = dib.create_label(function_md, "label2", file_md, 42, False)
|
|
br_inst = builder.br(foo_var_block)
|
|
dib.insert_label_before(foo_label2, foo_param_location, br_inst)
|
|
|
|
# Create non-preserved labels
|
|
dib.create_label(function_md, "label3", file_md, 42, True)
|
|
dib.create_label(function_md, "label4", file_md, 42, False)
|
|
|
|
# Finalize DIBuilder
|
|
dib.finalize()
|
|
|
|
# Build ret in vars block
|
|
builder.position_at_end(foo_var_block)
|
|
ret_inst = builder.ret(zero_val)
|
|
|
|
# Insert phi before ret using new positioning
|
|
insert_pos = foo_var_block.first_instruction
|
|
assert insert_pos is not None, (
|
|
"Expected at least one instruction in block"
|
|
)
|
|
builder.position_before(insert_pos, before_dbg=True)
|
|
phi1 = builder.phi(i64_type, "p1")
|
|
phi1.add_incoming(zero_val, foo_entry_block)
|
|
|
|
# Do it again with the other positioning function
|
|
builder.position_before(insert_pos, before_dbg=True)
|
|
phi2 = builder.phi(i64_type, "p2")
|
|
phi2.add_incoming(zero_val, foo_entry_block)
|
|
|
|
# Insert non-phi after ret's debug records and before ret.
|
|
builder.position_before(ret_inst, before_dbg=False)
|
|
add_inst = builder.add(phi1, phi2, "a")
|
|
|
|
# Iterate over debug records
|
|
add_dbg_first = add_inst.first_dbg_record
|
|
assert add_dbg_first is not None, "First debug record should exist"
|
|
add_dbg_second = add_dbg_first.next
|
|
assert add_dbg_second is not None, "Second debug record should exist"
|
|
add_dbg_last = add_inst.last_dbg_record
|
|
assert add_dbg_last is not None, "Last debug record should exist"
|
|
# Note: Debug records are opaque pointers, comparison may not work reliably
|
|
# assert add_dbg_second == add_dbg_last
|
|
add_dbg_over = add_dbg_second.next
|
|
assert add_dbg_over is None, "Should be no record after second"
|
|
add_dbg_first_prev = add_dbg_second.prev
|
|
assert add_dbg_first_prev is not None, "Previous of second should exist"
|
|
# assert add_dbg_first == add_dbg_first_prev
|
|
add_dbg_under = add_dbg_first_prev.prev
|
|
assert add_dbg_under is None, "Should be no record before first"
|
|
|
|
# Print module
|
|
print(str(mod))
|
|
|
|
return 0
|