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, force_enum = None): 77 self.name = name 78 self.start = start 79 self.size = size 80 self.implied = implied 81 self.is_enum = (force_enum is not None) or size > 1 82 self.enum = force_enum or name 83 84 if not self.is_enum: 85 self.bare_values = ['', name] 86 self.default = 0 87 else: 88 self.bare_values = [x.value for x in enums[self.enum].values] 89 defaults = [x for x in enums[self.enum].values if x.default] 90 assert(len(defaults) <= 1) 91 92 if len(defaults) > 0: 93 self.default = self.bare_values.index(defaults[0].value) 94 else: 95 self.default = None 96 97def Flag(name, start): 98 return Modifier(name, start, 1) 99 100# Model a single instruction 101class Source: 102 def __init__(self, index, size, is_float = False, swizzle = False, 103 halfswizzle = False, widen = False, lanes = False, combine = False, lane = None, absneg = False, notted = False, name = ""): 104 self.is_float = is_float or absneg 105 self.start = (index * 8) 106 self.size = size 107 self.absneg = absneg 108 self.notted = notted 109 self.swizzle = swizzle 110 self.halfswizzle = halfswizzle 111 self.widen = widen 112 self.lanes = lanes 113 self.lane = lane 114 self.combine = combine 115 self.name = name 116 117 self.offset = {} 118 self.bits = {} 119 if absneg: 120 self.offset['neg'] = 32 + 2 + ((2 - index) * 2) 121 self.offset['abs'] = 33 + 2 + ((2 - index) * 2) 122 self.bits['neg'] = 1 123 self.bits['abs'] = 1 124 if notted: 125 self.offset['not'] = 35 126 self.bits['not'] = 1 127 if widen or lanes or halfswizzle: 128 self.offset['widen'] = 26 if index == 1 else 36 129 self.bits['widen'] = 4 # XXX: too much? 130 if lane: 131 self.offset['lane'] = self.lane 132 self.bits['lane'] = 2 if size in (8, 32) else 1 133 if swizzle: 134 assert(size in [16, 32]) 135 self.offset['swizzle'] = 24 + ((2 - index) * 2) 136 self.bits['swizzle'] = 2 137 if combine: 138 self.offset['combine'] = 37 139 self.bits['combine'] = 3 140 141class Dest: 142 def __init__(self, name = ""): 143 self.name = name 144 145class Staging: 146 def __init__(self, read = False, write = False, count = 0, flags = 'true', name = ""): 147 self.name = name 148 self.read = read 149 self.write = write 150 self.count = count 151 self.flags = (flags != 'false') 152 self.start = 40 153 154 if write and not self.flags: 155 self.start = 16 156 157 # For compatibility 158 self.absneg = False 159 self.swizzle = False 160 self.notted = False 161 self.widen = False 162 self.lanes = False 163 self.lane = False 164 self.halfswizzle = False 165 self.combine = False 166 self.size = 32 167 168 if not self.flags: 169 self.encoded_flags = 0 170 elif flags == 'rw': 171 self.encoded_flags = 0xc0 172 else: 173 assert(flags == 'true') 174 self.encoded_flags = (0x80 if write else 0) | (0x40 if read else 0) 175 176class Immediate: 177 def __init__(self, name, start, size, signed): 178 self.name = name 179 self.start = start 180 self.size = size 181 self.signed = signed 182 183class Instruction: 184 def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None, unit = None): 185 self.name = name 186 self.srcs = srcs 187 self.dests = dests 188 self.opcode = opcode 189 self.opcode2 = opcode2 or 0 190 self.immediates = immediates 191 self.modifiers = modifiers 192 self.staging = staging 193 self.unit = unit 194 self.is_signed = len(name.split(".")) > 1 and ('s' in name.split(".")[1]) 195 196 # Message-passing instruction <===> not ALU instruction 197 self.message = unit not in ["FMA", "CVT", "SFU"] 198 199 self.secondary_shift = max(len(self.srcs) * 8, 16) 200 self.secondary_mask = 0xF if opcode2 is not None else 0x0 201 if "left" in [x.name for x in self.modifiers]: 202 self.secondary_mask |= 0x100 203 if len(srcs) == 3 and (srcs[1].widen or srcs[1].lanes): 204 self.secondary_mask &= ~0xC # conflicts 205 if opcode == 0x90: 206 # XXX: XMLify this, but disambiguates sign of conversions 207 self.secondary_mask |= 0x10 208 if name.startswith("LOAD.i") or name.startswith("STORE.i") or name.startswith("LD_BUFFER.i"): 209 self.secondary_shift = 27 # Alias with memory_size 210 self.secondary_mask = 0x7 211 if "descriptor_type" in [x.name for x in self.modifiers]: 212 self.secondary_mask = 0x3 213 self.secondary_shift = 37 214 elif "memory_width" in [x.name for x in self.modifiers]: 215 self.secondary_mask = 0x7 216 self.secondary_shift = 27 217 218 assert(len(dests) == 0 or not staging) 219 assert(not opcode2 or (opcode2 & self.secondary_mask) == opcode2) 220 221 def __str__(self): 222 return self.name 223 224# Build a single source from XML 225def build_source(el, i, size): 226 lane = el.get('lane', None) 227 if lane == "true": 228 lane = 38 if i == 0 else 36 229 elif lane is not None: 230 lane = int(lane) 231 232 return Source(i, int(el.get('size', size)), 233 absneg = el.get('absneg', False), 234 is_float = el.get('float', False), 235 swizzle = el.get('swizzle', False), 236 halfswizzle = el.get('halfswizzle', False), 237 widen = el.get('widen', False), 238 lanes = el.get('lanes', False), 239 combine = el.get('combine', False), 240 lane = lane, 241 notted = el.get('not', False), 242 name = el.text or "") 243 244def build_imm(el): 245 return Immediate(el.attrib['name'], int(el.attrib['start']), 246 int(el.attrib['size']), bool(el.attrib.get('signed', False))) 247 248def build_staging(i, el): 249 r = xmlbool(el.attrib.get('read', 'false')) 250 w = xmlbool(el.attrib.get('write', 'false')) 251 count = int(el.attrib.get('count', '0')) 252 flags = el.attrib.get('flags', 'true') 253 254 return Staging(r, w, count, flags, el.text or '') 255 256def build_modifier(el): 257 name = el.attrib['name'] 258 start = int(el.attrib['start']) 259 size = int(el.attrib['size']) 260 implied = xmlbool(el.get('implied', 'false')) 261 262 return Modifier(name, start, size, implied) 263 264# Build a single instruction from XML and group based overrides 265def build_instr(el, overrides = {}): 266 # Get overridables 267 name = overrides.get('name') or el.attrib.get('name') 268 opcode = overrides.get('opcode') or el.attrib.get('opcode') 269 opcode2 = overrides.get('opcode2') or el.attrib.get('opcode2') 270 unit = overrides.get('unit') or el.attrib.get('unit') 271 opcode = int(opcode, base=0) 272 opcode2 = int(opcode2, base=0) if opcode2 else None 273 274 # Get explicit sources/dests 275 tsize = typesize(name) 276 sources = [] 277 i = 0 278 279 for src in el.findall('src'): 280 built = build_source(src, i, tsize) 281 sources += [built] 282 283 # 64-bit sources in a 32-bit (message) instruction count as two slots 284 # Affects BLEND, ST_CVT 285 if tsize != 64 and built.size == 64: 286 i = i + 2 287 else: 288 i = i + 1 289 290 dests = [Dest(dest.text or '') for dest in el.findall('dest')] 291 292 # Get implicit ones 293 sources = sources + ([Source(i, int(tsize)) for i in range(int(el.attrib.get('srcs', 0)))]) 294 dests = dests + ([Dest()] * int(el.attrib.get('dests', 0))) 295 296 # Get staging registers 297 staging = [build_staging(i, el) for i, el in enumerate(el.findall('sr'))] 298 299 # Get immediates 300 imms = [build_imm(imm) for imm in el.findall('imm')] 301 302 modifiers = [] 303 for mod in el: 304 if mod.tag in MODIFIERS: 305 modifiers.append(MODIFIERS[mod.tag]) 306 elif mod.tag =='mod': 307 modifiers.append(build_modifier(mod)) 308 309 instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging, unit = unit) 310 311 instructions.append(instr) 312 313# Build all the instructions in a group by duplicating the group itself with 314# overrides for each distinct instruction 315def build_group(el): 316 for ins in el.findall('ins'): 317 build_instr(el, overrides = { 318 'name': ins.attrib['name'], 319 'opcode': ins.attrib.get('opcode'), 320 'opcode2': ins.attrib.get('opcode2'), 321 'unit': ins.attrib.get('unit'), 322 }) 323 324def to_alphanum(name): 325 substitutions = { 326 ' ': '_', 327 '/': '_', 328 '[': '', 329 ']': '', 330 '(': '', 331 ')': '', 332 '-': '_', 333 ':': '', 334 '.': '', 335 ',': '', 336 '=': '', 337 '>': '', 338 '#': '', 339 '&': '', 340 '*': '', 341 '"': '', 342 '+': '', 343 '\'': '', 344 } 345 346 for i, j in substitutions.items(): 347 name = name.replace(i, j) 348 349 return name 350 351def safe_name(name): 352 name = to_alphanum(name) 353 if not name[0].isalpha(): 354 name = '_' + name 355 356 return name.lower() 357 358# Parses out the size part of an opocde name 359def typesize(opcode): 360 if opcode[-3:] == '128': 361 return 128 362 if opcode[-2:] == '48': 363 return 48 364 elif opcode[-1] == '8': 365 return 8 366 else: 367 try: 368 return int(opcode[-2:]) 369 except: 370 return 32 371 372for child in root.findall('enum'): 373 enums[safe_name(child.attrib['name'])] = build_enum(child) 374 375MODIFIERS = { 376 # Texture instructions share a common encoding 377 "wide_indices": Flag("wide_indices", 8), 378 "array_enable": Flag("array_enable", 10), 379 "texel_offset": Flag("texel_offset", 11), 380 "shadow": Flag("shadow", 12), 381 "integer_coordinates": Flag("integer_coordinates", 13), 382 "fetch_component": Modifier("fetch_component", 14, 2), 383 "lod_mode": Modifier("lod_mode", 13, 3), 384 "lod_bias_disable": Modifier("lod_mode", 13, 1), 385 "lod_clamp_disable": Modifier("lod_mode", 14, 1), 386 "write_mask": Modifier("write_mask", 22, 4), 387 "register_type": Modifier("register_type", 26, 2), 388 "dimension": Modifier("dimension", 28, 2), 389 "skip": Flag("skip", 39), 390 "register_width": Modifier("register_width", 46, 1, force_enum = "register_width"), 391 "secondary_register_width": Modifier("secondary_register_width", 47, 1, force_enum = "register_width"), 392 "vartex_register_width": Modifier("varying_texture_register_width", 24, 2), 393 394 "atom_opc": Modifier("atomic_operation", 22, 4), 395 "atom_opc_1": Modifier("atomic_operation_with_1", 22, 4), 396 "inactive_result": Modifier("inactive_result", 22, 4), 397 "memory_access": Modifier("memory_access", 24, 2), 398 "regfmt": Modifier("register_format", 24, 3), 399 "source_format": Modifier("source_format", 24, 4), 400 "vecsize": Modifier("vector_size", 28, 2), 401 402 "slot": Modifier("slot", 30, 3), 403 "roundmode": Modifier("round_mode", 30, 2), 404 "result_type": Modifier("result_type", 30, 2), 405 "saturate": Flag("saturate", 30), 406 "not_result": Flag("not_result", 30), 407 408 "lane_op": Modifier("lane_operation", 32, 2), 409 "cmp": Modifier("condition", 32, 3), 410 "clamp": Modifier("clamp", 32, 2), 411 "sr_count": Modifier("staging_register_count", 33, 3, implied = True), 412 "sample_and_update": Modifier("sample_and_update_mode", 33, 3), 413 "sr_write_count": Modifier("staging_register_write_count", 36, 3, implied = True), 414 415 "conservative": Flag("conservative", 35), 416 "subgroup": Modifier("subgroup_size", 36, 4), 417 "update": Modifier("update_mode", 36, 2), 418 "sample": Modifier("sample_mode", 38, 2), 419} 420 421# Parse the ISA 422for child in root: 423 if child.tag == 'group': 424 build_group(child) 425 elif child.tag == 'ins': 426 build_instr(child) 427 428instruction_dict = { ins.name: ins for ins in instructions } 429 430# Validate there are no duplicated instructions 431if len(instruction_dict) != len(instructions): 432 import collections 433 counts = collections.Counter([i.name for i in instructions]) 434 for c in counts: 435 if counts[c] != 1: 436 print(f'{c} appeared {counts[c]} times.') 437 438assert(len(instruction_dict) == len(instructions)) 439