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