Skip to content

Commit

Permalink
Add support for self referential metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
aseyboldt committed Dec 6, 2022
1 parent ed0f625 commit dd24f4a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 7 deletions.
18 changes: 14 additions & 4 deletions llvmlite/ir/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,32 @@ def _fix_di_operands(self, operands):
fixed_ops.append((name, op))
return fixed_ops

def add_metadata(self, operands):
def add_metadata(self, operands, *, self_ref=False):
"""
Add an unnamed metadata to the module with the given *operands*
(a sequence of values) or return a previous equivalent metadata.
A MDValue instance is returned, it can then be associated to
e.g. an instruction.
A self referential metadata entry has itself as the first
operand to ensure uniquencess. These references are never
cached.
"""
if not isinstance(operands, (list, tuple)):
raise TypeError("expected a list or tuple of metadata values, "
"got %r" % (operands,))
operands = self._fix_metadata_operands(operands)
key = tuple(operands)
if key not in self._metadatacache:
if self_ref or key not in self._metadatacache:
n = len(self.metadata)
md = values.MDValue(self, operands, name=str(n))
self._metadatacache[key] = md
md = values.MDValue(
self,
operands,
name=str(n),
self_ref=self_ref,
)
if not self_ref:
self._metadatacache[key] = md
else:
md = self._metadatacache[key]
return md
Expand Down
17 changes: 14 additions & 3 deletions llvmlite/ir/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,11 +662,14 @@ class MDValue(NamedValue):
"""
name_prefix = '!'

def __init__(self, parent, values, name):
def __init__(self, parent, values, name, *, self_ref=False):
super(MDValue, self).__init__(parent,
types.MetaDataType(),
name=name)
self.operands = tuple(values)
# Store only the non-self params to avoid
# ref counting cycles
self._operands = tuple(values)
self._is_self_ref = self_ref
parent.metadata.append(self)

def descr(self, buf):
Expand All @@ -687,6 +690,8 @@ def _get_reference(self):

def __eq__(self, other):
if isinstance(other, MDValue):
if self._is_self_ref:
return self is other
return self.operands == other.operands
else:
return False
Expand All @@ -695,7 +700,13 @@ def __ne__(self, other):
return not self.__eq__(other)

def __hash__(self):
return hash(self.operands)
return hash(self._operands)

@property
def operands(self):
if self._is_self_ref:
return (self,) + self._operands
return self._operands


class DIToken:
Expand Down
11 changes: 11 additions & 0 deletions llvmlite/tests/test_ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,17 @@ def test_unnamed_metadata_3(self):
self.assert_ir_line('!1 = !{ i32 789 }', mod)
self.assert_ir_line('!2 = !{ i32 123, !0, !1, !0 }', mod)

def test_self_relf_metadata(self):
mod = self.module()
value = mod.add_metadata((), self_ref=True)
self.assert_ir_line('!0 = !{ !0 }', mod)
value2 = mod.add_metadata((value,))
assert value is not value2
self.assert_ir_line('!1 = !{ !0 }', mod)
value3 = mod.add_metadata((), self_ref=True)
self.assert_ir_line('!2 = !{ !2 }', mod)
assert value is not value3

def test_metadata_string(self):
# Escaping contents of a metadata string
mod = self.module()
Expand Down

0 comments on commit dd24f4a

Please sign in to comment.