|  | #!/usr/bin/env python3 | 
|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | from copy import deepcopy | 
|  | from typing import Any, Dict, Iterable, Optional | 
|  |  | 
|  | from .insn_yaml import Insn | 
|  | from .operand import RegOperandType | 
|  |  | 
|  |  | 
|  | def get_op_val_str(insn: Insn, op_vals: Dict[str, int], opname: str) -> str: | 
|  | '''Get the value of the given (register) operand as a string.''' | 
|  | op = insn.name_to_operand[opname] | 
|  | assert isinstance(op.op_type, RegOperandType) | 
|  | return op.op_type.op_val_to_str(op_vals[opname], None) | 
|  |  | 
|  |  | 
|  | class ConstantContext: | 
|  | '''Represents known-constant GPRs. | 
|  |  | 
|  | This datatype is used to track and evaluate GPR pointers for indirect | 
|  | references. | 
|  | ''' | 
|  | def __init__(self, values: Dict[str, int]): | 
|  | # The x0 register needs to always be 0 | 
|  | assert values.get('x0', None) == 0 | 
|  | self.values = values.copy() | 
|  |  | 
|  | @staticmethod | 
|  | def empty() -> 'ConstantContext': | 
|  | '''Represents a context with no known constants.''' | 
|  | return ConstantContext({'x0': 0}) | 
|  |  | 
|  | def set(self, gpr: str, value: int) -> None: | 
|  | '''Set the value of a GPR in the context.''' | 
|  | if gpr == 'x0': | 
|  | # Ignore writes to x0; it's read-only. | 
|  | return | 
|  | self.values[gpr] = value | 
|  |  | 
|  | def get(self, gpr: str) -> Optional[int]: | 
|  | '''Get the value of a GPR in the context.''' | 
|  | return self.values.get(gpr, None) | 
|  |  | 
|  | def __contains__(self, gpr: str) -> bool: | 
|  | return gpr in self.values | 
|  |  | 
|  | def __deepcopy__(self, memo: Optional[Dict[int, | 
|  | Any]]) -> 'ConstantContext': | 
|  | return ConstantContext(deepcopy(self.values, memo)) | 
|  |  | 
|  | def includes(self, other: 'ConstantContext') -> bool: | 
|  | '''Returns true iff other is a restriction of self.''' | 
|  | for k, v in other.values.items(): | 
|  | if self.get(k) != v: | 
|  | return False | 
|  | return True | 
|  |  | 
|  | def intersect(self, other: 'ConstantContext') -> 'ConstantContext': | 
|  | '''Returns a new context with only values on which self/other agree. | 
|  |  | 
|  | Does not modify self or other. | 
|  | ''' | 
|  | out = {} | 
|  | for k, v in self.values.items(): | 
|  | if other.get(k) == v: | 
|  | out[k] = v | 
|  | return ConstantContext(out) | 
|  |  | 
|  | def removemany(self, to_remove: Iterable[str]) -> None: | 
|  | '''Remove the given registers from the constant context. | 
|  |  | 
|  | Useful for cases when, for example, the given registers have been | 
|  | overwritten. Does nothing for registers that are not known constants, | 
|  | or for the special register x0. | 
|  | ''' | 
|  | for reg in to_remove: | 
|  | if reg != 'x0': | 
|  | self.values.pop(reg, None) | 
|  |  | 
|  | def update_insn(self, insn: Insn, op_vals: Dict[str, int]) -> None: | 
|  | '''Updates to new known constant values GPRs after the instruction. | 
|  |  | 
|  | Currently, this procedure supports only a limited set of instructions. | 
|  | Since constant values only need to be known in order to decode indirect | 
|  | references to WDRs and loop counts, this set is chosen based on operations | 
|  | likely to happen to those registers: `addi`, `lui`, and bignum instructions | 
|  | containing `_inc` op_vals. | 
|  | ''' | 
|  | new_values = {} | 
|  | if insn.mnemonic == 'addi': | 
|  | grs1_name = get_op_val_str(insn, op_vals, 'grs1') | 
|  | if grs1_name in self.values: | 
|  | grd_name = get_op_val_str(insn, op_vals, 'grd') | 
|  | # Operand is a constant; add/update grd | 
|  | new_values[grd_name] = self.values[grs1_name] + op_vals['imm'] | 
|  | elif insn.mnemonic == 'lui': | 
|  | grd_name = get_op_val_str(insn, op_vals, 'grd') | 
|  | new_values[grd_name] = op_vals['imm'] << 12 | 
|  | else: | 
|  | # If the instruction has any op_vals ending in _inc, | 
|  | # assume we're incrementing the corresponding register | 
|  | for op in insn.operands: | 
|  | if op.name.endswith('_inc') and op_vals[op.name] != 0: | 
|  | # If reg to be incremented is a constant, increment it | 
|  | inc_op = op.name[:-(len('_inc'))] | 
|  | inc_name = get_op_val_str(insn, op_vals, inc_op) | 
|  | if inc_name in self.values: | 
|  | new_values[inc_name] = self.values[inc_name] + 1 | 
|  |  | 
|  | # If the instruction's information-flow graph indicates that we updated any | 
|  | # constant register other than the ones handled above, the value of that | 
|  | # register can no longer be determined; remove it from the constants | 
|  | # dictionary. | 
|  | iflow = insn.iflow.evaluate(op_vals, self.values) | 
|  | self.removemany(iflow.all_sinks()) | 
|  |  | 
|  | self.values.update(new_values) |