blob: 73281c2b773b21dd6c6822e86f5fa68fe815f145 [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
'''A module defining a base class for a snippet generator.
The generators in the ./gens/ subdirectory all derive from this class. To
actually generate some snippets, use the wrapper in snippet_gens.py.
'''
from typing import Optional, Tuple
from shared.insn_yaml import Insn, InsnsFile
from .program import Program
from .model import Model
from .snippet import Snippet
class SnippetGen:
'''A parameterised sequence of instructions
These can be added to the instructions generated so far for a given random
binary.
'''
def gen(self,
size: int,
model: Model,
program: Program) -> Optional[Tuple[Snippet, bool, int]]:
'''Try to generate instructions for this type of snippet.
size is always positive and gives an upper bound on the number of
instructions in the dynamic instruction stream that this should
generate. For example, a loop of 10 instructions that goes around 10
times would consume 100 from size.
On success, inserts the instructions into program, updates the model,
and returns a tuple (snippet, done, new_size). snippet is the generated
snippet. done is true if the program is finished (if snippet ends with
ecall) and is false otherwise. new_size is the size left after the
generated snippet.
On failure, leaves program and model unchanged and returns None. There
should always be at least one snippet generator with positive weight
(see pick_weight below) that succeeds unconditionally. This will be the
ecall generator. Failure is interpreted as "this snippet won't work
with the current program state", but the generator may be retried
later.
'''
raise NotImplementedError('gen not implemented by subclass')
def pick_weight(self,
size: int,
model: Model,
program: Program) -> float:
'''Pick a weight by which to multiply this generator's default weight
This is called for each generator before we start trying to generate a
snippet for a given program and model state. This can be used to
disable a generator when we know it won't work (if size is too small, for
example).
It can also be used to alter weights depending on where we are in the
program. For example, a generator that generates ecall to end the
program could decrease its weight when size is large, to avoid
generating tiny programs by accident.
The default implementation always returns 1.0.
'''
return 1.0
def _get_named_insn(self, insns_file: InsnsFile, mnemonic: str) -> Insn:
'''Get an instruction from insns_file by mnemonic
This is used for specialized snippets that need to generate a specific
instruction and wraps the error handling for when someone has removed
the instruction from the file.
'''
insn = insns_file.mnemonic_to_insn.get(mnemonic.lower())
if insn is None:
raise RuntimeError('No {} instruction in instructions file.'
.format(mnemonic.upper()))
return insn