pysvp64asm: avoid empty fields
[openpower-isa.git] / src / openpower / sv / sv_binutils_fptrans.py
1 import argparse as _argparse
2 import dataclasses as _dataclasses
3 import enum as _enum
4 import functools as _functools
5
6
7 from openpower.decoder.power_enums import (
8 FPTRANS_INSNS as _FPTRANS_INSNS,
9 find_wiki_dir as _find_wiki_dir,
10 )
11 from openpower.decoder.power_insn import (
12 Database as _Database,
13 StaticOperand as _StaticOperand,
14 WordInstruction as _WordInstruction,
15 )
16
17
18 @_dataclasses.dataclass(eq=True, frozen=True)
19 class StaticOperand:
20 name: str
21 value: int
22 span: tuple
23
24
25 @_dataclasses.dataclass(eq=True, frozen=True)
26 class DynamicOperand:
27 name: str
28 span: tuple
29
30
31 @_functools.total_ordering
32 @_dataclasses.dataclass(eq=True, frozen=True)
33 class Entry:
34 name: str
35 static_operands: tuple
36 dynamic_operands: tuple
37
38 def __lt__(self, other):
39 if not isinstance(other, self.__class__):
40 return NotImplemented
41 return ((self.PO, self.XO, self.Rc) <
42 (other.PO, other.XO, other.Rc))
43
44 @property
45 def PO(self):
46 for operand in self.static_operands:
47 if operand.name == "PO":
48 return operand.value
49 raise ValueError(self)
50
51 @property
52 def XO(self):
53 for operand in self.static_operands:
54 if operand.name == "XO":
55 return operand.value
56 raise ValueError(self)
57
58 @property
59 def Rc(self):
60 for operand in self.static_operands:
61 if operand.name == "Rc":
62 return operand.value
63 raise ValueError(self)
64
65
66 def collect(db):
67 fptrans = tuple(_FPTRANS_INSNS)
68 fptrans_Rc = tuple(map(lambda name: f"{name}.", fptrans))
69
70 def fptrans_match(insn):
71 return ((insn.name in fptrans) or (insn.name in fptrans_Rc))
72
73 for record in filter(fptrans_match, db):
74 if len(record.opcodes) > 1:
75 raise NotImplementedError(record.opcodes)
76 PO = record.section.opcode
77 if PO is None:
78 PO = tuple(record.ppc)[0].opcode
79 XO = None
80 else:
81 XO = tuple(record.ppc)[0].opcode
82
83 @_dataclasses.dataclass(eq=True, frozen=True)
84 class POStaticOperand(_StaticOperand):
85 @property
86 def span(self):
87 return tuple(range(0, 6))
88
89 @_dataclasses.dataclass(eq=True, frozen=True)
90 class XOStaticOperand(_StaticOperand):
91 @property
92 def span(self):
93 return tuple(self.record.section.bitsel)
94
95 static_operands = [(POStaticOperand, {
96 "name": "PO",
97 "value": (PO.value & PO.mask)
98 })]
99 if XO is not None:
100 static_operands.append((XOStaticOperand, {
101 "name": "XO",
102 "value": (XO.value & XO.mask)
103 }))
104
105 static_operands.extend(record.mdwn.operands.static)
106 dynamic_operands = record.mdwn.operands.dynamic
107
108 def static_operand(operand):
109 (cls, kwargs) = operand
110 operand = cls(record=record, **kwargs)
111 return StaticOperand(name=operand.name,
112 value=operand.value, span=operand.span)
113
114 def dynamic_operand(operand):
115 (cls, kwargs) = operand
116 operand = cls(record=record, **kwargs)
117 return DynamicOperand(name=operand.name,
118 span=operand.span)
119
120 static_operands = tuple(map(static_operand, static_operands))
121 dynamic_operands = tuple(map(dynamic_operand, dynamic_operands))
122
123 yield Entry(name=record.name,
124 static_operands=static_operands,
125 dynamic_operands=dynamic_operands)
126
127
128 def opcodes(entry):
129 operands = entry.dynamic_operands
130 operands = ", ".join(operand.name for operand in operands)
131 string = ",\t".join((
132 f"\"{entry.name}\"",
133 f"XRC({entry.PO},{entry.XO},{entry.Rc})",
134 "X_MASK",
135 "SVP64",
136 "PPCVLE",
137 f"{{{operands}}}",
138 ))
139 return f"{{{string}}},"
140
141
142 def asm(entry, binutils=False, regex=False):
143 operands = tuple(entry.dynamic_operands)
144 for (idx, operand) in enumerate(operands):
145 values = []
146 for each in operands:
147 if binutils and each.name in ("FRT", "FRA", "FRB"):
148 values.append("f0")
149 elif binutils and each.name in ("RT", "RA", "RB"):
150 values.append("r0")
151 else:
152 values.append("0")
153 value = str((1 << len(operand.span)) - 1)
154 if binutils and operand.name in ("FRT", "FRA", "FRB"):
155 value = f"f{value}"
156 elif binutils and operand.name in ("RT", "RA", "RB"):
157 value = f"r{value}"
158 values[idx] = value
159 sep = "\s+" if regex else " "
160 yield f"{entry.name}{sep}{','.join(values)}"
161
162
163 def dis(entry, binutils=True):
164 def objdump(byte):
165 return f"{byte:02x}"
166
167 asm_plain = tuple(asm(entry, binutils=binutils, regex=False))
168 asm_regex = tuple(asm(entry, binutils=binutils, regex=True))
169 for (idx, dynamic_operand) in enumerate(entry.dynamic_operands):
170 insn = _WordInstruction.integer(value=0)
171 for static_operand in entry.static_operands:
172 span = static_operand.span
173 insn[span] = static_operand.value
174 span = dynamic_operand.span
175 insn[span] = ((1 << len(span)) - 1)
176 if binutils:
177 big = " ".join(map(objdump, insn.bytes(byteorder="big")))
178 little = " ".join(map(objdump, insn.bytes(byteorder="little")))
179 yield f".*:\s+({big}|{little})\s+{asm_regex[idx]}"
180 else:
181 yield asm_plain[idx]
182
183
184 class Mode(_enum.Enum):
185 OPCODES = "opcodes"
186 ASM = "asm"
187 DIS = "dis"
188
189 def __str__(self):
190 return self.name.lower()
191
192
193 if __name__ == "__main__":
194 parser = _argparse.ArgumentParser()
195 parser.add_argument("mode", type=Mode, choices=Mode)
196 args = dict(vars(parser.parse_args()))
197
198 mode = args["mode"]
199 db = _Database(_find_wiki_dir())
200 entries = sorted(collect(db))
201
202 generator = {
203 Mode.OPCODES: opcodes,
204 Mode.ASM: asm,
205 Mode.DIS: dis,
206 }[mode]
207 if mode is Mode.DIS:
208 print("#as: -mlibresoc")
209 print("#objdump: -dr -Mlibresoc")
210 print("")
211 print(".*: file format .*")
212 print("")
213 print("")
214 print("Disassembly of section \\.text:")
215 print("0+ <\.text>:")
216
217 if mode in {Mode.ASM, Mode.DIS}:
218 for subgenerator in map(generator, entries):
219 for line in subgenerator:
220 print(line)
221 else:
222 for line in map(generator, entries):
223 print(line)