hdl._ir: remember origins of a fragment during elaboration.
This isn't expected to result in a significant increase in memory use, so for now it's enabled by default. Elaboration chains where it is not desired to preserve origins can delete the `origins` attribute from the fragment and nothing will be stored. The interface `Fragment.origins` remains private, as is the rest of the `Fragment` interface (including itself), but it enables certain codebases that currently use a much more invasive technique to rely on reading a single private field.
This commit is contained in:
parent
c40cfc9fb5
commit
09029cdd91
|
@ -31,8 +31,11 @@ class Fragment:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(obj, platform):
|
def get(obj, platform):
|
||||||
code = None
|
code = None
|
||||||
|
origins = []
|
||||||
while True:
|
while True:
|
||||||
if isinstance(obj, Fragment):
|
if isinstance(obj, Fragment):
|
||||||
|
if hasattr(obj, "origins"):
|
||||||
|
obj.origins = tuple(origins)
|
||||||
return obj
|
return obj
|
||||||
elif isinstance(obj, Elaboratable):
|
elif isinstance(obj, Elaboratable):
|
||||||
code = obj.elaborate.__code__
|
code = obj.elaborate.__code__
|
||||||
|
@ -58,6 +61,7 @@ class Fragment:
|
||||||
category=UserWarning,
|
category=UserWarning,
|
||||||
filename=code.co_filename,
|
filename=code.co_filename,
|
||||||
lineno=code.co_firstlineno)
|
lineno=code.co_firstlineno)
|
||||||
|
origins.append(obj)
|
||||||
obj = new_obj
|
obj = new_obj
|
||||||
|
|
||||||
def __init__(self, *, src_loc=None):
|
def __init__(self, *, src_loc=None):
|
||||||
|
@ -70,6 +74,7 @@ class Fragment:
|
||||||
self.generated = OrderedDict()
|
self.generated = OrderedDict()
|
||||||
self.flatten = False
|
self.flatten = False
|
||||||
self.src_loc = src_loc
|
self.src_loc = src_loc
|
||||||
|
self.origins = None
|
||||||
|
|
||||||
def add_ports(self, *ports, dir):
|
def add_ports(self, *ports, dir):
|
||||||
assert dir in ("i", "o", "io")
|
assert dir in ("i", "o", "io")
|
||||||
|
|
|
@ -4,6 +4,7 @@ from collections import OrderedDict
|
||||||
|
|
||||||
from amaranth.hdl._ast import *
|
from amaranth.hdl._ast import *
|
||||||
from amaranth.hdl._cd import *
|
from amaranth.hdl._cd import *
|
||||||
|
from amaranth.hdl._dsl import *
|
||||||
from amaranth.hdl._ir import *
|
from amaranth.hdl._ir import *
|
||||||
from amaranth.hdl._mem import *
|
from amaranth.hdl._mem import *
|
||||||
|
|
||||||
|
@ -919,3 +920,29 @@ class InstanceTestCase(FHDLTestCase):
|
||||||
f: ("top",),
|
f: ("top",),
|
||||||
a_f: ("top", "a$U$0")
|
a_f: ("top", "a$U$0")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class ElaboratesTo(Elaboratable):
|
||||||
|
def __init__(self, lower):
|
||||||
|
self.lower = lower
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
return self.lower
|
||||||
|
|
||||||
|
|
||||||
|
class OriginsTestCase(FHDLTestCase):
|
||||||
|
def test_origins(self):
|
||||||
|
elab1 = ElaboratesTo(elab2 := ElaboratesTo(m := Module()))
|
||||||
|
frag = Fragment.get(elab1, platform=None)
|
||||||
|
self.assertEqual(len(frag.origins), 3)
|
||||||
|
self.assertIsInstance(frag.origins, tuple)
|
||||||
|
self.assertIs(frag.origins[0], elab1)
|
||||||
|
self.assertIs(frag.origins[1], elab2)
|
||||||
|
self.assertIs(frag.origins[2], m)
|
||||||
|
|
||||||
|
def test_origins_disable(self):
|
||||||
|
inst = Instance("test")
|
||||||
|
del inst.origins
|
||||||
|
elab = ElaboratesTo(inst)
|
||||||
|
frag = Fragment.get(elab, platform=None)
|
||||||
|
self.assertFalse(hasattr(frag, "_origins"))
|
Loading…
Reference in a new issue