1#encoding=utf-8 2 3# Copyright (C) 2021 Collabora, Ltd. 4# 5# Permission is hereby granted, free of charge, to any person obtaining a 6# copy of this software and associated documentation files (the "Software"), 7# to deal in the Software without restriction, including without limitation 8# the rights to use, copy, modify, merge, publish, distribute, sublicense, 9# and/or sell copies of the Software, and to permit persons to whom the 10# Software is furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice (including the next 13# paragraph) shall be included in all copies or substantial portions of the 14# Software. 15# 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22# IN THE SOFTWARE. 23 24import argparse 25import sys 26import struct 27from valhall import instructions, enums, immediates, typesize 28 29LINE = '' 30 31class ParseError(Exception): 32 def __init__(self, error): 33 self.error = error 34 35class FAUState: 36 def __init__(self, mode): 37 self.mode = mode 38 self.uniform_slot = None 39 self.special = None 40 self.buffer = set() 41 42 def push(self, s): 43 self.buffer.add(s) 44 die_if(len(self.buffer) > 2, "Overflowed FAU buffer") 45 46 def push_special(self, s): 47 die_if(self.special is not None and self.special != s, 48 'Multiple special immediates') 49 self.special = s 50 self.push(s) 51 52 def descriptor(self, s): 53 die_if(self.mode != 'none', f'Expected no modifier with {s}') 54 self.push_special(s) 55 56 def uniform(self, v): 57 slot = v >> 1 58 59 die_if(self.mode != 'none', 60 'Expected uniform with default immediate mode') 61 die_if(self.uniform_slot is not None and self.uniform_slot != slot, 62 'Overflowed uniform slots') 63 self.uniform_slot = slot 64 self.push(f'uniform{v}') 65 66 def id(self, s): 67 die_if(self.mode != 'id', 68 'Expected .id modifier with thread storage pointer') 69 70 self.push_special(f'id{s}') 71 72 def ts(self, s): 73 die_if(self.mode != 'ts', 74 'Expected .ts modifier with thread pointer') 75 self.push_special(f'ts{s}') 76 77 def constant(self, cons): 78 self.push(cons) 79 80# When running standalone, exit with the error since we're dealing with a 81# human. Otherwise raise a Python exception so the test harness can handle it. 82def die(s): 83 if __name__ == "__main__": 84 print(LINE) 85 print(s) 86 sys.exit(1) 87 else: 88 raise ParseError(s) 89 90def die_if(cond, s): 91 if cond: 92 die(s) 93 94def parse_int(s, minimum, maximum): 95 try: 96 number = int(s, base = 0) 97 except ValueError: 98 die(f"Expected number {s}") 99 100 if number > maximum or number < minimum: 101 die(f"Range error on {s}") 102 103 return number 104 105def encode_source(op, fau): 106 if op == 'atest_datum': 107 fau.descriptor(op) 108 return 0x2A | 0xC0 109 elif op.startswith('blend_descriptor_'): 110 fau.descriptor(op) 111 fin = op[len('blend_descriptor_'):] 112 die_if(len(fin) != 3, 'Bad syntax') 113 die_if(fin[1] != '_', 'Bad syntax') 114 die_if(fin[2] not in ['x', 'y'], 'Bad component') 115 116 rt = parse_int(fin[0], 0, 7) 117 hi = 1 if (fin[2] == 'y') else 0 118 return (0x30 | (2*rt) + hi) | 0xC0 119 elif op[0] == '`': 120 die_if(op[1] != 'r', f"Expected register after discard {op}") 121 return parse_int(op[2:], 0, 63) | 0x40 122 elif op[0] == 'r': 123 return parse_int(op[1:], 0, 63) 124 elif op[0] == 'u': 125 val = parse_int(op[1:], 0, 63) 126 fau.uniform(val) 127 return val | 0x80 128 elif op[0] == 'i': 129 return int(op[3:]) | 0xC0 130 elif op in enums['thread_storage_pointers'].bare_values: 131 fau.ts(op) 132 idx = 32 + enums['thread_storage_pointers'].bare_values.index(op) 133 return idx | 0xC0 134 elif op in enums['thread_identification'].bare_values: 135 fau.id(op) 136 idx = 32 + enums['thread_identification'].bare_values.index(op) 137 return idx | 0xC0 138 elif op.startswith('0x'): 139 try: 140 val = int(op, base=0) 141 except ValueError: 142 die('Expected value') 143 144 die_if(val not in immediates, 'Unexpected immediate value') 145 fau.constant(val) 146 return immediates.index(val) | 0xC0 147 else: 148 die('Invalid operand') 149 150def encode_dest(op): 151 die_if(op[0] != 'r', f"Expected register destination {op}") 152 153 parts = op.split(".") 154 reg = parts[0] 155 156 # Default to writing in full 157 wrmask = 0x3 158 159 if len(parts) > 1: 160 WMASKS = ["h0", "h1"] 161 die_if(len(parts) > 2, "Too many modifiers") 162 mask = parts[1]; 163 die_if(mask not in WMASKS, "Expected a write mask") 164 wrmask = 1 << WMASKS.index(mask) 165 166 return parse_int(reg[1:], 0, 63) | (wrmask << 6) 167 168def parse_asm(line): 169 global LINE 170 LINE = line # For better errors 171 encoded = 0 172 173 # Figure out mnemonic 174 head = line.split(" ")[0] 175 opts = [ins for ins in instructions if head.startswith(ins.name)] 176 opts = sorted(opts, key=lambda x: len(x.name), reverse=True) 177 178 if len(opts) == 0: 179 die(f"No known mnemonic for {head}") 180 181 if len(opts) > 1 and len(opts[0].name) == len(opts[1].name): 182 print(f"Ambiguous mnemonic for {head}") 183 print(f"Options:") 184 for ins in opts: 185 print(f" {ins}") 186 sys.exit(1) 187 188 ins = opts[0] 189 190 # Split off modifiers 191 if len(head) > len(ins.name) and head[len(ins.name)] != '.': 192 die(f"Expected . after instruction in {head}") 193 194 mods = head[len(ins.name) + 1:].split(".") 195 modifier_map = {} 196 immediate_mode = 'none' 197 198 for mod in mods: 199 if mod in enums['immediate_mode'].bare_values: 200 die_if(immediate_mode != 'none', 'Multiple immediate modes specified') 201 immediate_mode = mod 202 203 tail = line[(len(head) + 1):] 204 operands = [x.strip() for x in tail.split(",") if len(x.strip()) > 0] 205 expected_op_count = len(ins.srcs) + len(ins.dests) + len(ins.immediates) + len(ins.staging) 206 if len(operands) != expected_op_count: 207 die(f"Wrong number of operands in {line}, expected {expected_op_count}, got {len(operands)} {operands}") 208 209 # Encode each operand 210 for i, (op, sr) in enumerate(zip(operands, ins.staging)): 211 die_if(op[0] != '@', f'Expected staging register, got {op}') 212 parts = op[1:].split(':') 213 214 die_if(any([x[0] != 'r' for x in parts]), f'Expected registers, got {op}') 215 regs = [parse_int(x[1:], 0, 63) for x in parts] 216 217 sr_count = len(regs) 218 die_if(sr_count < 1, f'Expected staging register, got {op}') 219 die_if(sr_count > 7, f'Too many staging registers {sr_count}') 220 221 base = regs[0] 222 die_if(any([reg != (base + i) for i, reg in enumerate(regs)]), 223 'Expected consecutive staging registers, got {op}') 224 225 if sr.count == 0: 226 modifier_map["staging_register_count"] = sr_count 227 else: 228 die_if(sr_count != sr.count, f"Expected 4 staging registers, got {sr_count}") 229 230 encoded |= ((sr.encoded_flags | base) << sr.start) 231 operands = operands[len(ins.staging):] 232 233 for op, dest in zip(operands, ins.dests): 234 encoded |= encode_dest(op) << 40 235 operands = operands[len(ins.dests):] 236 237 if len(ins.dests) == 0 and len(ins.staging) == 0: 238 # Set a placeholder writemask to prevent encoding faults 239 encoded |= (0xC0 << 40) 240 241 fau = FAUState(immediate_mode) 242 243 for i, (op, src) in enumerate(zip(operands, ins.srcs)): 244 parts = op.split('.') 245 encoded |= encode_source(parts[0], fau) << (i * 8) 246 247 # Has a swizzle been applied yet? 248 swizzled = False 249 250 for mod in parts[1:]: 251 # Encode the modifier 252 if mod in src.offset and src.bits[mod] == 1: 253 encoded |= (1 << src.offset[mod]) 254 elif mod in enums[f'swizzles_{src.size}_bit'].bare_values and (src.widen or src.lanes): 255 die_if(swizzled, "Multiple swizzles specified") 256 swizzled = True 257 val = enums[f'swizzles_{src.size}_bit'].bare_values.index(mod) 258 encoded |= (val << src.offset['widen']) 259 elif src.lane and mod in enums[f'lane_{src.size}_bit'].bare_values: 260 die_if(swizzled, "Multiple swizzles specified") 261 swizzled = True 262 val = enums[f'lane_{src.size}_bit'].bare_values.index(mod) 263 encoded |= (val << src.offset['lane']) 264 elif src.size == 32 and mod in enums['widen'].bare_values: 265 die_if(not src.swizzle, "Instruction doesn't take widens") 266 die_if(swizzled, "Multiple swizzles specified") 267 swizzled = True 268 val = enums['widen'].bare_values.index(mod) 269 encoded |= (val << src.offset['swizzle']) 270 elif src.size == 16 and mod in enums['swizzles_16_bit'].bare_values: 271 die_if(not src.swizzle, "Instruction doesn't take swizzles") 272 die_if(swizzled, "Multiple swizzles specified") 273 swizzled = True 274 val = enums['swizzles_16_bit'].bare_values.index(mod) 275 encoded |= (val << src.offset['swizzle']) 276 elif mod in enums['lane_8_bit'].bare_values: 277 die_if(not src.lane, "Instruction doesn't take a lane") 278 die_if(swizzled, "Multiple swizzles specified") 279 swizzled = True 280 val = enums['lane_8_bit'].bare_values.index(mod) 281 encoded |= (val << src.lane) 282 else: 283 die(f"Unknown modifier {mod}") 284 285 # Encode the identity if a swizzle is required but not specified 286 if src.swizzle and not swizzled and src.size == 16: 287 mod = enums['swizzles_16_bit'].default 288 val = enums['swizzles_16_bit'].bare_values.index(mod) 289 encoded |= (val << src.offset['swizzle']) 290 elif src.widen and not swizzled and src.size == 16: 291 die_if(swizzled, "Multiple swizzles specified") 292 mod = enums['swizzles_16_bit'].default 293 val = enums['swizzles_16_bit'].bare_values.index(mod) 294 encoded |= (val << src.offset['widen']) 295 296 operands = operands[len(ins.srcs):] 297 298 for i, (op, imm) in enumerate(zip(operands, ins.immediates)): 299 if op[0] == '#': 300 die_if(imm.name != 'constant', "Wrong syntax for immediate") 301 parts = [imm.name, op[1:]] 302 else: 303 parts = op.split(':') 304 die_if(len(parts) != 2, f"Wrong syntax for immediate, wrong number of colons in {op}") 305 die_if(parts[0] != imm.name, f"Wrong immediate, expected {imm.name}, got {parts[0]}") 306 307 if imm.signed: 308 minimum = -(1 << (imm.size - 1)) 309 maximum = +(1 << (imm.size - 1)) - 1 310 else: 311 minimum = 0 312 maximum = (1 << imm.size) - 1 313 314 val = parse_int(parts[1], minimum, maximum) 315 316 if val < 0: 317 # Sign extends 318 val = (1 << imm.size) + val 319 320 encoded |= (val << imm.start) 321 322 operands = operands[len(ins.immediates):] 323 324 # Encode the operation itself 325 encoded |= (ins.opcode << 48) 326 encoded |= (ins.opcode2 << ins.secondary_shift) 327 328 # Encode modifiers 329 has_action = False 330 for mod in mods: 331 if len(mod) == 0: 332 continue 333 334 if mod in enums['action'].bare_values: 335 die_if(has_action, "Multiple actions specified") 336 has_action = True 337 encoded |= (enums['action'].bare_values.index(mod) << 59) 338 encoded |= (1 << 62) # Action, not wait 339 elif mod.startswith('wait'): 340 die_if(has_action, "Multiple actions specified") 341 has_action = True 342 343 slots = mod[len('wait'):] 344 try: 345 slots = set([int(x) for x in slots]) 346 except ValueError: 347 die(f"Expected slots in {mod}") 348 349 known_slots = set([0, 1, 2]) 350 die_if(not slots.issubset(known_slots), f"Unknown slots in {mod}") 351 352 if 0 in slots: 353 encoded |= (1 << 59) 354 if 1 in slots: 355 encoded |= (1 << 60) 356 if 2 in slots: 357 encoded |= (1 << 61) 358 elif mod in enums['immediate_mode'].bare_values: 359 pass # handled specially 360 else: 361 candidates = [c for c in ins.modifiers if mod in c.bare_values] 362 363 die_if(len(candidates) == 0, f"Invalid modifier {mod} used") 364 assert(len(candidates) == 1) # No ambiguous modifiers 365 opts = candidates[0] 366 367 value = opts.bare_values.index(mod) 368 assert(value is not None) 369 370 die_if(opts.name in modifier_map, f"{opts.name} specified twice") 371 modifier_map[opts.name] = value 372 373 for mod in ins.modifiers: 374 value = modifier_map.get(mod.name, mod.default) 375 die_if(value is None, f"Missing required modifier {mod.name}") 376 377 assert(value < (1 << mod.size)) 378 encoded |= (value << mod.start) 379 380 encoded |= (enums['immediate_mode'].bare_values.index(immediate_mode) << 57) 381 return encoded 382 383if __name__ == "__main__": 384 # Provide commandline interface 385 parser = argparse.ArgumentParser(description='Assemble Valhall shaders') 386 parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), 387 default=sys.stdin) 388 parser.add_argument('outfile', type=argparse.FileType('wb')) 389 args = parser.parse_args() 390 391 lines = args.infile.read().strip().split('\n') 392 lines = [l for l in lines if len(l) > 0 and l[0] != '#'] 393 394 packed = b''.join([struct.pack('<Q', parse_asm(ln)) for ln in lines]) 395 args.outfile.write(packed) 396