[otbn] Define helper functions for snippet merging in RIG
It turns out that "try to merge the next snippet and make a
SeqSnippet otherwise" is a reasonably standard operation. Make
Snippet.merge do that instead.
The only time we need the "clever" function is when building up a big
list of snippets. Factor that logic out of SnippetGens into a static
merge_list function.
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/util/rig/gens/branch.py b/hw/ip/otbn/util/rig/gens/branch.py
index f3705e2..3d10bf9 100644
--- a/hw/ip/otbn/util/rig/gens/branch.py
+++ b/hw/ip/otbn/util/rig/gens/branch.py
@@ -11,7 +11,7 @@
from .jump import Jump
from ..program import ProgInsn, Program
from ..model import Model
-from ..snippet import BranchSnippet, ProgSnippet, SeqSnippet
+from ..snippet import BranchSnippet, ProgSnippet
from ..snippet_gen import GenCont, GenRet, SnippetGen
@@ -218,8 +218,7 @@
return None
jmp_snippet, model0 = jump_ret
- if not snippet0.merge(jmp_snippet):
- snippet0 = SeqSnippet([snippet0, jmp_snippet])
+ snippet0 = snippet0.merge(jmp_snippet)
else:
# Add the jump to go from branch 1 to branch 0
jump_ret = self.jump_gen.gen_tgt(model1, prog1, model0.pc)
@@ -227,8 +226,7 @@
return None
jmp_snippet, model1 = jump_ret
- if not snippet1.merge(jmp_snippet):
- snippet1 = SeqSnippet([snippet1, jmp_snippet])
+ snippet1 = snippet1.merge(jmp_snippet)
assert model0.pc == model1.pc
model0.merge(model1)
diff --git a/hw/ip/otbn/util/rig/snippet.py b/hw/ip/otbn/util/rig/snippet.py
index 4a53ad3..f283794 100644
--- a/hw/ip/otbn/util/rig/snippet.py
+++ b/hw/ip/otbn/util/rig/snippet.py
@@ -71,7 +71,7 @@
raise ValueError('Snippet {} has unknown key {!r}.'
.format(idx, key))
- def merge(self, snippet: 'Snippet') -> bool:
+ def _merge(self, snippet: 'Snippet') -> bool:
'''Merge snippet after this one and return True if possible.
If not possible, leaves self unchanged and returns False.
@@ -79,6 +79,40 @@
'''
return False
+ def merge(self, snippet: 'Snippet') -> 'Snippet':
+ '''Merge snippet after this one
+
+ On a successful merge, this will alter and return self. If a merge
+ isn't possible, this generates and returns a SeqSnippet.
+
+ '''
+ if self._merge(snippet):
+ return self
+
+ return SeqSnippet([self, snippet])
+
+ @staticmethod
+ def merge_list(snippets: List['Snippet']) -> 'Snippet':
+ '''Merge a non-empty list of snippets as much as possible'''
+ assert snippets
+ acc = []
+ cur = snippets[0]
+ for snippet in snippets[1:]:
+ if cur._merge(snippet):
+ continue
+
+ acc.append(cur)
+ cur = snippet
+
+ acc.append(cur)
+ return SeqSnippet(acc)
+
+ @staticmethod
+ def cons_option(snippet0: Optional['Snippet'],
+ snippet1: 'Snippet') -> 'Snippet':
+ '''Cons together one or two snippets'''
+ return snippet1 if snippet0 is None else snippet0.merge(snippet1)
+
def to_program(self) -> Program:
'''Write a series of disjoint snippets to make a program'''
# Find the size of the memory that we can access. Both memories start
@@ -136,7 +170,7 @@
return ProgSnippet(addr, insns)
- def merge(self, snippet: Snippet) -> bool:
+ def _merge(self, snippet: Snippet) -> bool:
if not isinstance(snippet, ProgSnippet):
return False
diff --git a/hw/ip/otbn/util/rig/snippet_gens.py b/hw/ip/otbn/util/rig/snippet_gens.py
index cd0f625..3293eff 100644
--- a/hw/ip/otbn/util/rig/snippet_gens.py
+++ b/hw/ip/otbn/util/rig/snippet_gens.py
@@ -9,7 +9,7 @@
from .program import Program
from .model import Model
-from .snippet import SeqSnippet, Snippet
+from .snippet import Snippet
from .snippet_gen import GenRet, SnippetGen
from .gens.branch import Branch
@@ -110,11 +110,7 @@
break
snippet, next_model = gr
-
- # Merge adjacent program snippets if possible. Otherwise, add a new
- # one.
- if not children or not children[-1].merge(snippet):
- children.append(snippet)
+ children.append(snippet)
if next_model is None:
break
@@ -125,5 +121,5 @@
assert ecall is False
return None
- snippet = children[0] if len(children) == 1 else SeqSnippet(children)
+ snippet = Snippet.merge_list(children)
return (snippet, next_model)