1#encoding=utf-8 2 3# Copyright (C) 2016 Intel Corporation 4# Copyright (C) 2016 Broadcom 5# Copyright (C) 2020 Collabora, Ltd. 6# 7# Permission is hereby granted, free of charge, to any person obtaining a 8# copy of this software and associated documentation files (the "Software"), 9# to deal in the Software without restriction, including without limitation 10# the rights to use, copy, modify, merge, publish, distribute, sublicense, 11# and/or sell copies of the Software, and to permit persons to whom the 12# Software is furnished to do so, subject to the following conditions: 13# 14# The above copyright notice and this permission notice (including the next 15# paragraph) shall be included in all copies or substantial portions of the 16# Software. 17# 18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24# IN THE SOFTWARE. 25 26import os 27import textwrap 28import xml.etree.ElementTree as ET 29import sys 30 31tree = ET.parse(os.path.join(os.path.dirname(__file__), 'ISA.xml')) 32root = tree.getroot() 33 34# All instructions in the ISA 35instructions = [] 36 37# All immediates in the ISA 38ilut = root.findall('lut')[0] 39assert(ilut.attrib['name'] == "Immediates") 40immediates = [int(imm.text, base=0) for imm in ilut.findall('constant')] 41enums = {} 42 43def xmlbool(s): 44 assert(s.lower() in ["false", "true"]) 45 return False if s.lower() == "false" else True 46 47class EnumValue: 48 def __init__(self, value, default): 49 self.value = value 50 self.default = default 51 52class Enum: 53 def __init__(self, name, values): 54 self.name = name 55 self.values = values 56 self.bare_values = [x.value for x in values] 57 58 defaults = [x.value for x in values if x.default] 59 if len(defaults) > 0: 60 assert(len(defaults) == 1) 61 self.default = defaults[0] 62 63def build_enum(el): 64 values = [] 65 66 for child in el: 67 if child.tag == 'value': 68 is_default = child.attrib.get('default', False) 69 values.append(EnumValue(child.text, is_default)) 70 elif child.tag == 'reserved': 71 values.append(EnumValue("reserved", False)) 72 73 return Enum(el.attrib['name'], values) 74 75class Modifier: 76 def __init__(self, name, start, size, implied = False): 77 self.name = name 78 self.start = start 79 self.size = size 80 self.implied = implied 81 82 if size == 1: 83 self.bare_values = ['', name] 84 self.default = 0 85 else: 86 enum = enums[name] 87 self.bare_values = [x.value for x in enum.values] 88 defaults = [x for x in enum.values if x.default] 89 assert(len(defaults) <= 1) 90 91 if len(defaults) > 0: 92 self.default = self.bare_values.index(defaults[0].value) 93 else: 94 self.default = None 95 96def Flag(name, start): 97 return Modifier(name, start, 1) 98 99# Model a single instruction 100class Source: 101 def __init__(self, index, size, is_float = False, swizzle = False, widen = False, lanes = False, lane = None, absneg = False, notted = False, name = ""): 102 self.is_float = is_float or absneg 103 self.size = size 104 self.absneg = absneg 105 self.notted = notted 106 self.swizzle = swizzle 107 self.widen = widen 108 self.lanes = lanes 109 self.lane = lane 110 self.name = name 111 112 self.offset = {} 113 self.bits = {} 114 if absneg: 115 self.offset['neg'] = 32 + 2 + ((2 - index) * 2) 116 self.offset['abs'] = 33 + 2 + ((2 - index) * 2) 117 self.bits['neg'] = 1 118 self.bits['abs'] = 1 119 if notted: 120 self.offset['not'] = 35 121 self.bits['not'] = 1 122 if widen or lanes: 123 self.offset['widen'] = 26 if index == 1 else 36 124 self.bits['widen'] = 4 # XXX: too much? 125 if lane: 126 self.offset['lane'] = self.lane 127 self.bits['lane'] = 2 if size in (8, 32) else 1 128 if swizzle: 129 assert(size in [16, 32]) 130 self.offset['swizzle'] = 24 + ((2 - index) * 2) 131 self.bits['swizzle'] = 2 132 133class Dest: 134 def __init__(self, name = ""): 135 self.name = name 136 137class Staging: 138 def __init__(self, read = False, write = False, index = 0, count = 0, flags = True, name = ""): 139 self.name = name 140 self.read = read 141 self.write = write 142 self.count = count 143 self.flags = flags 144 145 # For compatibility 146 self.absneg = False 147 self.swizzle = False 148 self.notted = False 149 self.widen = False 150 self.lanes = False 151 self.lane = False 152 self.size = 32 153 154 assert(index < 2) 155 self.start = 40 if index == 0 else 16 156 157 if not flags: 158 self.encoded_flags = 0 159 elif index > 0: 160 self.encoded_flags = 0xC0 161 else: 162 self.encoded_flags = (0x80 if write else 0) | (0x40 if read else 0) 163 164class Immediate: 165 def __init__(self, name, start, size, signed): 166 self.name = name 167 self.start = start 168 self.size = size 169 self.signed = signed 170 171class Instruction: 172 def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None): 173 self.name = name 174 self.srcs = srcs 175 self.dests = dests 176 self.opcode = opcode 177 self.opcode2 = opcode2 or 0 178 self.immediates = immediates 179 self.modifiers = modifiers 180 self.staging = staging 181 182 self.secondary_shift = max(len(self.srcs) * 8, 16) 183 self.secondary_mask = 0xF if opcode2 is not None else 0x0 184 if "left" in [x.name for x in self.modifiers]: 185 self.secondary_mask |= 0x100 186 if len(srcs) == 3 and (srcs[1].widen or srcs[1].lanes): 187 self.secondary_mask &= ~0xC # conflicts 188 if opcode == 0x90: 189 # XXX: XMLify this, but disambiguates sign of conversions 190 self.secondary_mask |= 0x10 191 if name.startswith("LOAD.i") or name.startswith("STORE.i"): 192 self.secondary_shift = 27 # Alias with memory_size 193 self.secondary_mask = 0x7 194 195 assert(len(dests) == 0 or not staging) 196 assert(not opcode2 or (opcode2 & self.secondary_mask) == opcode2) 197 198 def __str__(self): 199 return self.name 200 201# Build a single source from XML 202def build_source(el, i, size): 203 lane = el.get('lane', None) 204 if lane == "true": 205 lane = 38 if i == 0 else 36 206 elif lane is not None: 207 lane = int(lane) 208 209 return Source(i, int(el.get('size', size)), 210 absneg = el.get('absneg', False), 211 is_float = el.get('float', False), 212 swizzle = el.get('swizzle', False), 213 widen = el.get('widen', False), 214 lanes = el.get('lanes', False), 215 lane = lane, 216 notted = el.get('not', False), 217 name = el.text or "") 218 219def build_imm(el): 220 return Immediate(el.attrib['name'], int(el.attrib['start']), 221 int(el.attrib['size']), bool(el.attrib.get('signed', False))) 222 223def build_staging(i, el): 224 r = xmlbool(el.attrib.get('read', 'false')) 225 w = xmlbool(el.attrib.get('write', 'false')) 226 count = int(el.attrib.get('count', '0')) 227 flags = xmlbool(el.attrib.get('flags', 'true')) 228 229 return Staging(r, w, i, count, flags, el.text or '') 230 231def build_modifier(el): 232 name = el.attrib['name'] 233 start = int(el.attrib['start']) 234 size = int(el.attrib['size']) 235 implied = xmlbool(el.get('implied', 'false')) 236 237 return Modifier(name, start, size, implied) 238 239# Build a single instruction from XML and group based overrides 240def build_instr(el, overrides = {}): 241 # Get overridables 242 name = overrides.get('name') or el.attrib.get('name') 243 opcode = overrides.get('opcode') or el.attrib.get('opcode') 244 opcode2 = overrides.get('opcode2') or el.attrib.get('opcode2') 245 opcode = int(opcode, base=0) 246 opcode2 = int(opcode2, base=0) if opcode2 else None 247 248 # Get explicit sources/dests 249 tsize = typesize(name) 250 sources = [build_source(src, i, tsize) for i, src in enumerate(el.findall('src'))] 251 dests = [Dest(dest.text or '') for dest in el.findall('dest')] 252 253 # Get implicit ones 254 sources = sources + ([Source(i, int(tsize)) for i in range(int(el.attrib.get('srcs', 0)))]) 255 dests = dests + ([Dest()] * int(el.attrib.get('dests', 0))) 256 257 # Get staging registers 258 staging = [build_staging(i, el) for i, el in enumerate(el.findall('sr'))] 259 260 # Get immediates 261 imms = [build_imm(imm) for imm in el.findall('imm')] 262 263 modifiers = [] 264 for mod in el: 265 if mod.tag in MODIFIERS: 266 modifiers.append(MODIFIERS[mod.tag]) 267 elif mod.tag =='mod': 268 modifiers.append(build_modifier(mod)) 269 270 instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging) 271 272 instructions.append(instr) 273 274# Build all the instructions in a group by duplicating the group itself with 275# overrides for each distinct instruction 276def build_group(el): 277 for ins in el.findall('ins'): 278 build_instr(el, overrides = { 279 'name': ins.attrib['name'], 280 'opcode': ins.attrib.get('opcode'), 281 'opcode2': ins.attrib.get('opcode2'), 282 }) 283 284def to_alphanum(name): 285 substitutions = { 286 ' ': '_', 287 '/': '_', 288 '[': '', 289 ']': '', 290 '(': '', 291 ')': '', 292 '-': '_', 293 ':': '', 294 '.': '', 295 ',': '', 296 '=': '', 297 '>': '', 298 '#': '', 299 '&': '', 300 '*': '', 301 '"': '', 302 '+': '', 303 '\'': '', 304 } 305 306 for i, j in substitutions.items(): 307 name = name.replace(i, j) 308 309 return name 310 311def safe_name(name): 312 name = to_alphanum(name) 313 if not name[0].isalpha(): 314 name = '_' + name 315 316 return name.lower() 317 318# Parses out the size part of an opocde name 319def typesize(opcode): 320 if opcode[-3:] == '128': 321 return 128 322 if opcode[-2:] == '48': 323 return 48 324 elif opcode[-1] == '8': 325 return 8 326 else: 327 try: 328 return int(opcode[-2:]) 329 except: 330 return 32 331 332for child in root.findall('enum'): 333 enums[safe_name(child.attrib['name'])] = build_enum(child) 334 335MODIFIERS = { 336 "inactive_result": Modifier("inactive_result", 22, 4), 337 "store_segment": Modifier("store_segment", 24, 2), 338 "regfmt": Modifier("register_format", 24, 3), 339 "vecsize": Modifier("vector_size", 28, 2), 340 341 "slot": Modifier("slot", 30, 3), 342 "roundmode": Modifier("round_mode", 30, 2), 343 "result_type": Modifier("result_type", 30, 2), 344 "saturate": Flag("saturate", 30), 345 "not_result": Flag("not_result", 30), 346 347 "lane_op": Modifier("lane_operation", 32, 2), 348 "cmp": Modifier("condition", 32, 3), 349 "clamp": Modifier("clamp", 32, 2), 350 "sr_count": Modifier("staging_register_count", 33, 3, implied = True), 351 352 "subgroup": Modifier("subgroup_size", 36, 2), 353} 354 355# Parse the ISA 356for child in root: 357 if child.tag == 'group': 358 build_group(child) 359 elif child.tag == 'ins': 360 build_instr(child) 361 362instruction_dict = { ins.name: ins for ins in instructions } 363 364# Validate there are no duplicated instructions 365if len(instruction_dict) != len(instructions): 366 import collections 367 counts = collections.Counter([i.name for i in instructions]) 368 for c in counts: 369 if counts[c] != 1: 370 print(f'{c} appeared {counts[c]} times.') 371 372assert(len(instruction_dict) == len(instructions)) 373