llvmpy/llvm/debuginfo.py

398 lines
13 KiB
Python

"""
Support for debug info metadata.
"""
import functools
import llvm.core
from llvm import _dwarf
#----------------------------------------------------------------------------
# Some types and type checking functions
#----------------------------------------------------------------------------
int32_t = llvm.core.Type.int(32)
bool_t = llvm.core.Type.int(1)
p_int32_t = llvm.core.Type.pointer(int32_t)
null = llvm.core.Constant.null(p_int32_t)
i32 = functools.partial(llvm.core.Constant.int, int32_t)
i1 = functools.partial(llvm.core.Constant.int, bool_t)
def is_md(value):
return isinstance(value, (llvm.core.MetaData, llvm.core.NamedMetaData,
llvm.core.Value))
def is_mdstr(value):
return isinstance(value, (llvm.core.MetaDataString, llvm.core.Value))
#----------------------------------------------------------------------------
# Callbacks for debug type descriptors
#----------------------------------------------------------------------------
def get_i32(llvm_module, value):
return i32(value)
def get_i1(llvm_module, value):
return i1(value)
def get_md(llvm_module, value):
if is_md(value):
return value
return value.get_metadata(llvm_module)
def get_mdstr(llvm_module, value):
if is_mdstr(value):
return value
return llvm.core.MetaDataString.get(llvm_module, value)
def get_mdlist(llvm_module, value):
if isinstance(value, list):
value = MDList([value])
return get_md(llvm_module, value)
def get_lfunc(llvm_module, lfunc):
assert lfunc.module is llvm_module
return lfunc
#----------------------------------------------------------------------------
# Debug descriptors that generate LLVM Metadata
#----------------------------------------------------------------------------
class desc(object):
"""
Descriptor of a debug field.
field_name:
name of the field (keyword argument name)
build_metadata:
metadata callback :: (llvm_module, Python value) -> LLVM Value
default:
default value for this field
"""
def __init__(self, field_name, build_metadata, default=None):
self.field_name = field_name
self.build_metadata = build_metadata
self.default = default
def build_operand_list(type_descriptors, idx2name, operands, kwargs):
"""
Build the list of operands from the positional and keyword arguments
to a DebugInfoDescriptor.
E.g. FileDescriptor("foo.c", "/path/to/file", compile_unit=compile_unit)
idx2name: {0 : 'source_filename',
1 : 'source_filedir',
2 : 'compile_unit' }
operands: ["foo.c", "/path/to/file"]
kwargs: { 'compile_unit' : compile_unit }
"""
for i in range(len(operands), len(type_descriptors)):
name = idx2name[i]
if name in kwargs:
op = kwargs[name]
else:
typedesc = type_descriptors[i]
assert typedesc.default is not None, (
"No value found for field %s" % typedesc.field_name)
op = typedesc.default
operands.append(op)
return operands
class DebugInfoDescriptor(object):
"""
Base class to describe a debug info descriptor.
See http://llvm.org/docs/SourceLevelDebugging.html
"""
type_descriptors = None # [desc(...)]
idx2name = None # { "field_idx" : field_name }
name2idx = None # { "field_name" : field_idx }
# Whether the debug descriptor is allowed to be incomplete
# The policy seems unclear ?
accept_optional_data = False
def __init__(self, *args, **kwargs):
self.class_init()
self.metadata_cache = {}
operands = list(args)
if kwargs or not self.accept_optional_data:
operands = build_operand_list(self.type_descriptors,
self.idx2name, operands, kwargs)
self.operands = operands
self.operands_dict = dict(
(typedesc.field_name, operand)
for typedesc, operand in zip(self.type_descriptors, operands))
@classmethod
def class_init(cls):
if cls.idx2name is None and cls.type_descriptors is not None:
cls.name2idx = {}
cls.idx2name = {}
for i, typedesc in enumerate(cls.type_descriptors):
cls.name2idx[typedesc.field_name] = i
cls.idx2name[i] = typedesc.field_name
def add_metadata(self, metadata_name, metadata):
"""
Replace a metadata value in the operand list.
"""
idx = self.name2idx[metadata_name]
self.operands[idx] = metadata
#------------------------------------------------------------------------
# Define metadata in a module
#------------------------------------------------------------------------
def get_metadata(self, llvm_module):
"""
Get an existing metadata node for the given LLVM module, or build
one from this instance.
"""
if llvm_module in self.metadata_cache:
node = self.metadata_cache[llvm_module]
else:
node = self.build_metadata(llvm_module)
self.metadata_cache[llvm_module] = node
return node
def build_metadata(self, llvm_module):
"Build a metadata node for the given LLVM module"
mdops = []
for type_desc, operand in zip(self.type_descriptors, self.operands):
try:
field_value = type_desc.build_metadata(llvm_module, operand)
except Exception, e:
if type_desc.build_metadata is get_md:
raise
raise ValueError("Invalid value for field %r: %s" % (
type_desc.field_name, e))
mdops.append(field_value)
return llvm.core.MetaData.get(llvm_module, mdops)
def define(self, llvm_module):
"""
Define this debug descriptor as named debug metadata for the module.
"""
md = self.get_metadata(llvm_module)
llvm.core.MetaData.add_named_operand(llvm_module, "dbg", md)
#----------------------------------------------------------------------------
# Convenience descriptors
#----------------------------------------------------------------------------
class MDList(DebugInfoDescriptor):
"""
[MetaData]
"""
def __init__(self, operands):
self.type_descriptors = [desc("op", get_md)] * len(operands)
super(MDList, self).__init__(*operands)
empty = MDList([])
#----------------------------------------------------------------------------
# Dwarf Debug descriptors
#----------------------------------------------------------------------------
class CompileUnitDescriptor(DebugInfoDescriptor):
"""
!0 = metadata !{
i32, ;; Tag = 17 + LLVMDebugVersion (DW_TAG_compile_unit)
i32, ;; Unused field.
i32, ;; DWARF language identifier (ex. DW_LANG_C89)
metadata, ;; Source file name
metadata, ;; Source file directory (includes trailing slash)
metadata ;; Producer (ex. "4.0.1 LLVM (LLVM research group)")
i1, ;; True if this is a main compile unit.
i1, ;; True if this is optimized.
metadata, ;; Flags
i32 ;; Runtime version
metadata ;; List of enums types
metadata ;; List of retained types
metadata ;; List of subprograms
metadata ;; List of global variables
}
"""
accept_optional_data = True
type_descriptors = [
desc("tag", get_i32),
desc("unused", get_i32),
# Positional argument list starts here:
desc("langid", get_i32),
desc("source_filename", get_mdstr),
desc("source_filedir", get_mdstr),
desc("producer", get_mdstr),
desc("is_main", get_i1, default=False),
desc("is_optimized", get_i1, default=False),
desc("compile_flags", get_mdstr, default=""),
desc("runtime_version", get_i32, default=0),
desc("enum_types", get_mdlist, default=empty),
desc("retained_types", get_mdlist, default=empty),
desc("subprograms", get_mdlist, default=empty),
desc("global_vars", get_mdlist, default=empty),
]
def __init__(self, *args, **kwargs):
super(CompileUnitDescriptor, self).__init__(
_dwarf.DW_TAG_compile_unit + _dwarf.LLVMDebugVersion, # tag
0, # unused
*args, **kwargs)
def define(self, llvm_module):
"""
Define this debug descriptor as named debug metadata for the module.
"""
md = self.get_metadata(llvm_module)
llvm.core.MetaData.add_named_operand(llvm_module, "llvm.dbg.cu", md)
class FileDescriptor(DebugInfoDescriptor):
"""
!0 = metadata !{
i32, ;; Tag = 41 + LLVMDebugVersion (DW_TAG_file_type)
metadata, ;; Source file name
metadata, ;; Source file directory (includes trailing slash)
metadata ;; Unused
}
"""
type_descriptors = [
desc("tag", get_i32),
# Positional argument list starts here:
desc("source_filename", get_mdstr),
desc("source_filedir", get_mdstr),
desc("compile_unit", get_md, default=null),
]
def __init__(self, *args, **kwargs):
super(FileDescriptor, self).__init__(
_dwarf.DW_TAG_file_type + _dwarf.LLVMDebugVersion, # Tag
*args, **kwargs)
@classmethod
def from_compileunit(cls, compile_unit_descriptor):
return cls(compile_unit_descriptor.operands_dict["source_filename"],
compile_unit_descriptor.operands_dict["source_filedir"],
compile_unit_descriptor)
class SubprogramDescriptor(DebugInfoDescriptor):
"""
!2 = metadata !{
i32, ;; Tag = 46 + LLVMDebugVersion (DW_TAG_subprogram)
i32, ;; Unused field.
metadata, ;; Reference to context descriptor
metadata, ;; Name
metadata, ;; Display name (fully qualified C++ name)
metadata, ;; MIPS linkage name (for C++)
metadata, ;; Reference to file where defined
i32, ;; Line number where defined
metadata, ;; Reference to type descriptor
i1, ;; True if the global is local to compile unit (static)
i1, ;; True if the global is defined in the compile unit (not extern)
i32, ;; Line number where the scope of the subprogram begins
i32, ;; Virtuality, e.g. dwarf::DW_VIRTUALITY__virtual
i32, ;; Index into a virtual function
metadata, ;; indicates which base type contains the vtable pointer for the
;; derived class
i32, ;; Flags - Artifical, Private, Protected, Explicit, Prototyped.
i1, ;; isOptimized
Function * , ;; Pointer to LLVM function
metadata, ;; Lists function template parameters
metadata, ;; Function declaration descriptor
metadata ;; List of function variables
}
"""
accept_optional_data = True
type_descriptors = [
desc("tag", get_i32),
desc("unused", get_i32),
# Positional argument list starts here:
desc("file_desc", get_md),
desc("name", get_mdstr),
desc("display_name", get_mdstr),
desc("mips_linkage_name", get_mdstr),
desc("source_file_ref", get_md),
desc("line_number", get_i32),
desc("signature", get_md),
desc("is_local", get_i1, default=False),
desc("is_definition", get_i1, default=True),
desc("virtual_attribute", get_i32, default=0),
desc("virtual_index", get_i32, default=0),
desc("virttab", get_i32, default=0),
desc("flags", get_i32, default=0),
desc("is_optimized", get_i1, default=False),
desc("llvm_func", get_lfunc),
# Template params
# Func decl
# Func vars
]
def __init__(self, *args, **kwargs):
super(SubprogramDescriptor, self).__init__(
_dwarf.DW_TAG_subprogram + _dwarf.LLVMDebugVersion, # Tag
0, # Unused
*args, **kwargs)
class BlockDescriptor(DebugInfoDescriptor):
"""
!3 = metadata !{
i32, ;; Tag = 11 + LLVMDebugVersion (DW_TAG_lexical_block)
metadata,;; Reference to context descriptor
i32, ;; Line number
i32 ;; Column number
}
"""
type_descriptors = [
desc("tag", get_i32),
# Positional argument list starts here:
desc("context_descr", get_md), # FileDescr | BlockDescr |
# SubprogDescr | ComputeUnit
desc("line_number", get_i32),
desc("col_number", get_i32),
]
def __init__(self, *args, **kwargs):
super(BlockDescriptor, self).__init__(
_dwarf.DW_TAG_lexical_block + _dwarf.LLVMDebugVersion,
*args, **kwargs)
class PositionInfoDescriptor(DebugInfoDescriptor):
"""
line number, column number, scope, and original scope
"""
type_descriptors = [
desc("line_number", get_i32),
desc("col_number", get_i32),
desc("context_descr", get_md), # Scope of instruction
# (FileDescr etc)
desc("original_context_descr", get_md, # The original scope if inlined
default=null),
]