17ec681f3Smrg#encoding=utf-8 27ec681f3Smrg 37ec681f3Smrg# Copyright (C) 2021 Collabora, Ltd. 47ec681f3Smrg# 57ec681f3Smrg# Permission is hereby granted, free of charge, to any person obtaining a 67ec681f3Smrg# copy of this software and associated documentation files (the "Software"), 77ec681f3Smrg# to deal in the Software without restriction, including without limitation 87ec681f3Smrg# the rights to use, copy, modify, merge, publish, distribute, sublicense, 97ec681f3Smrg# and/or sell copies of the Software, and to permit persons to whom the 107ec681f3Smrg# Software is furnished to do so, subject to the following conditions: 117ec681f3Smrg# 127ec681f3Smrg# The above copyright notice and this permission notice (including the next 137ec681f3Smrg# paragraph) shall be included in all copies or substantial portions of the 147ec681f3Smrg# Software. 157ec681f3Smrg# 167ec681f3Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 177ec681f3Smrg# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 187ec681f3Smrg# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 197ec681f3Smrg# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207ec681f3Smrg# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 217ec681f3Smrg# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 227ec681f3Smrg# IN THE SOFTWARE. 237ec681f3Smrg 247ec681f3Smrgimport argparse 257ec681f3Smrgimport sys 267ec681f3Smrgimport struct 277ec681f3Smrgfrom valhall import instructions, enums, immediates, typesize 287ec681f3Smrg 297ec681f3SmrgLINE = '' 307ec681f3Smrg 317ec681f3Smrgclass ParseError(Exception): 327ec681f3Smrg def __init__(self, error): 337ec681f3Smrg self.error = error 347ec681f3Smrg 357ec681f3Smrgclass FAUState: 367ec681f3Smrg def __init__(self, mode): 377ec681f3Smrg self.mode = mode 387ec681f3Smrg self.uniform_slot = None 397ec681f3Smrg self.special = None 407ec681f3Smrg self.buffer = set() 417ec681f3Smrg 427ec681f3Smrg def push(self, s): 437ec681f3Smrg self.buffer.add(s) 447ec681f3Smrg die_if(len(self.buffer) > 2, "Overflowed FAU buffer") 457ec681f3Smrg 467ec681f3Smrg def push_special(self, s): 477ec681f3Smrg die_if(self.special is not None and self.special != s, 487ec681f3Smrg 'Multiple special immediates') 497ec681f3Smrg self.special = s 507ec681f3Smrg self.push(s) 517ec681f3Smrg 527ec681f3Smrg def descriptor(self, s): 537ec681f3Smrg die_if(self.mode != 'none', f'Expected no modifier with {s}') 547ec681f3Smrg self.push_special(s) 557ec681f3Smrg 567ec681f3Smrg def uniform(self, v): 577ec681f3Smrg slot = v >> 1 587ec681f3Smrg 597ec681f3Smrg die_if(self.mode != 'none', 607ec681f3Smrg 'Expected uniform with default immediate mode') 617ec681f3Smrg die_if(self.uniform_slot is not None and self.uniform_slot != slot, 627ec681f3Smrg 'Overflowed uniform slots') 637ec681f3Smrg self.uniform_slot = slot 647ec681f3Smrg self.push(f'uniform{v}') 657ec681f3Smrg 667ec681f3Smrg def id(self, s): 677ec681f3Smrg die_if(self.mode != 'id', 687ec681f3Smrg 'Expected .id modifier with thread storage pointer') 697ec681f3Smrg 707ec681f3Smrg self.push_special(f'id{s}') 717ec681f3Smrg 727ec681f3Smrg def ts(self, s): 737ec681f3Smrg die_if(self.mode != 'ts', 747ec681f3Smrg 'Expected .ts modifier with thread pointer') 757ec681f3Smrg self.push_special(f'ts{s}') 767ec681f3Smrg 777ec681f3Smrg def constant(self, cons): 787ec681f3Smrg self.push(cons) 797ec681f3Smrg 807ec681f3Smrg# When running standalone, exit with the error since we're dealing with a 817ec681f3Smrg# human. Otherwise raise a Python exception so the test harness can handle it. 827ec681f3Smrgdef die(s): 837ec681f3Smrg if __name__ == "__main__": 847ec681f3Smrg print(LINE) 857ec681f3Smrg print(s) 867ec681f3Smrg sys.exit(1) 877ec681f3Smrg else: 887ec681f3Smrg raise ParseError(s) 897ec681f3Smrg 907ec681f3Smrgdef die_if(cond, s): 917ec681f3Smrg if cond: 927ec681f3Smrg die(s) 937ec681f3Smrg 947ec681f3Smrgdef parse_int(s, minimum, maximum): 957ec681f3Smrg try: 967ec681f3Smrg number = int(s, base = 0) 977ec681f3Smrg except ValueError: 987ec681f3Smrg die(f"Expected number {s}") 997ec681f3Smrg 1007ec681f3Smrg if number > maximum or number < minimum: 1017ec681f3Smrg die(f"Range error on {s}") 1027ec681f3Smrg 1037ec681f3Smrg return number 1047ec681f3Smrg 1057ec681f3Smrgdef encode_source(op, fau): 1067ec681f3Smrg if op == 'atest_datum': 1077ec681f3Smrg fau.descriptor(op) 1087ec681f3Smrg return 0x2A | 0xC0 1097ec681f3Smrg elif op.startswith('blend_descriptor_'): 1107ec681f3Smrg fau.descriptor(op) 1117ec681f3Smrg fin = op[len('blend_descriptor_'):] 1127ec681f3Smrg die_if(len(fin) != 3, 'Bad syntax') 1137ec681f3Smrg die_if(fin[1] != '_', 'Bad syntax') 1147ec681f3Smrg die_if(fin[2] not in ['x', 'y'], 'Bad component') 1157ec681f3Smrg 1167ec681f3Smrg rt = parse_int(fin[0], 0, 7) 1177ec681f3Smrg hi = 1 if (fin[2] == 'y') else 0 1187ec681f3Smrg return (0x30 | (2*rt) + hi) | 0xC0 1197ec681f3Smrg elif op[0] == '`': 1207ec681f3Smrg die_if(op[1] != 'r', f"Expected register after discard {op}") 1217ec681f3Smrg return parse_int(op[2:], 0, 63) | 0x40 1227ec681f3Smrg elif op[0] == 'r': 1237ec681f3Smrg return parse_int(op[1:], 0, 63) 1247ec681f3Smrg elif op[0] == 'u': 1257ec681f3Smrg val = parse_int(op[1:], 0, 63) 1267ec681f3Smrg fau.uniform(val) 1277ec681f3Smrg return val | 0x80 1287ec681f3Smrg elif op[0] == 'i': 1297ec681f3Smrg return int(op[3:]) | 0xC0 1307ec681f3Smrg elif op in enums['thread_storage_pointers'].bare_values: 1317ec681f3Smrg fau.ts(op) 1327ec681f3Smrg idx = 32 + enums['thread_storage_pointers'].bare_values.index(op) 1337ec681f3Smrg return idx | 0xC0 1347ec681f3Smrg elif op in enums['thread_identification'].bare_values: 1357ec681f3Smrg fau.id(op) 1367ec681f3Smrg idx = 32 + enums['thread_identification'].bare_values.index(op) 1377ec681f3Smrg return idx | 0xC0 1387ec681f3Smrg elif op.startswith('0x'): 1397ec681f3Smrg try: 1407ec681f3Smrg val = int(op, base=0) 1417ec681f3Smrg except ValueError: 1427ec681f3Smrg die('Expected value') 1437ec681f3Smrg 1447ec681f3Smrg die_if(val not in immediates, 'Unexpected immediate value') 1457ec681f3Smrg fau.constant(val) 1467ec681f3Smrg return immediates.index(val) | 0xC0 1477ec681f3Smrg else: 1487ec681f3Smrg die('Invalid operand') 1497ec681f3Smrg 1507ec681f3Smrgdef encode_dest(op): 1517ec681f3Smrg die_if(op[0] != 'r', f"Expected register destination {op}") 1527ec681f3Smrg 1537ec681f3Smrg parts = op.split(".") 1547ec681f3Smrg reg = parts[0] 1557ec681f3Smrg 1567ec681f3Smrg # Default to writing in full 1577ec681f3Smrg wrmask = 0x3 1587ec681f3Smrg 1597ec681f3Smrg if len(parts) > 1: 1607ec681f3Smrg WMASKS = ["h0", "h1"] 1617ec681f3Smrg die_if(len(parts) > 2, "Too many modifiers") 1627ec681f3Smrg mask = parts[1]; 1637ec681f3Smrg die_if(mask not in WMASKS, "Expected a write mask") 1647ec681f3Smrg wrmask = 1 << WMASKS.index(mask) 1657ec681f3Smrg 1667ec681f3Smrg return parse_int(reg[1:], 0, 63) | (wrmask << 6) 1677ec681f3Smrg 1687ec681f3Smrgdef parse_asm(line): 1697ec681f3Smrg global LINE 1707ec681f3Smrg LINE = line # For better errors 1717ec681f3Smrg encoded = 0 1727ec681f3Smrg 1737ec681f3Smrg # Figure out mnemonic 1747ec681f3Smrg head = line.split(" ")[0] 1757ec681f3Smrg opts = [ins for ins in instructions if head.startswith(ins.name)] 1767ec681f3Smrg opts = sorted(opts, key=lambda x: len(x.name), reverse=True) 1777ec681f3Smrg 1787ec681f3Smrg if len(opts) == 0: 1797ec681f3Smrg die(f"No known mnemonic for {head}") 1807ec681f3Smrg 1817ec681f3Smrg if len(opts) > 1 and len(opts[0].name) == len(opts[1].name): 1827ec681f3Smrg print(f"Ambiguous mnemonic for {head}") 1837ec681f3Smrg print(f"Options:") 1847ec681f3Smrg for ins in opts: 1857ec681f3Smrg print(f" {ins}") 1867ec681f3Smrg sys.exit(1) 1877ec681f3Smrg 1887ec681f3Smrg ins = opts[0] 1897ec681f3Smrg 1907ec681f3Smrg # Split off modifiers 1917ec681f3Smrg if len(head) > len(ins.name) and head[len(ins.name)] != '.': 1927ec681f3Smrg die(f"Expected . after instruction in {head}") 1937ec681f3Smrg 1947ec681f3Smrg mods = head[len(ins.name) + 1:].split(".") 1957ec681f3Smrg modifier_map = {} 1967ec681f3Smrg immediate_mode = 'none' 1977ec681f3Smrg 1987ec681f3Smrg for mod in mods: 1997ec681f3Smrg if mod in enums['immediate_mode'].bare_values: 2007ec681f3Smrg die_if(immediate_mode != 'none', 'Multiple immediate modes specified') 2017ec681f3Smrg immediate_mode = mod 2027ec681f3Smrg 2037ec681f3Smrg tail = line[(len(head) + 1):] 2047ec681f3Smrg operands = [x.strip() for x in tail.split(",") if len(x.strip()) > 0] 2057ec681f3Smrg expected_op_count = len(ins.srcs) + len(ins.dests) + len(ins.immediates) + len(ins.staging) 2067ec681f3Smrg if len(operands) != expected_op_count: 2077ec681f3Smrg die(f"Wrong number of operands in {line}, expected {expected_op_count}, got {len(operands)} {operands}") 2087ec681f3Smrg 2097ec681f3Smrg # Encode each operand 2107ec681f3Smrg for i, (op, sr) in enumerate(zip(operands, ins.staging)): 2117ec681f3Smrg die_if(op[0] != '@', f'Expected staging register, got {op}') 2127ec681f3Smrg parts = op[1:].split(':') 2137ec681f3Smrg 2147ec681f3Smrg die_if(any([x[0] != 'r' for x in parts]), f'Expected registers, got {op}') 2157ec681f3Smrg regs = [parse_int(x[1:], 0, 63) for x in parts] 2167ec681f3Smrg 2177ec681f3Smrg sr_count = len(regs) 2187ec681f3Smrg die_if(sr_count < 1, f'Expected staging register, got {op}') 2197ec681f3Smrg die_if(sr_count > 7, f'Too many staging registers {sr_count}') 2207ec681f3Smrg 2217ec681f3Smrg base = regs[0] 2227ec681f3Smrg die_if(any([reg != (base + i) for i, reg in enumerate(regs)]), 2237ec681f3Smrg 'Expected consecutive staging registers, got {op}') 2247ec681f3Smrg 2257ec681f3Smrg if sr.count == 0: 2267ec681f3Smrg modifier_map["staging_register_count"] = sr_count 2277ec681f3Smrg else: 2287ec681f3Smrg die_if(sr_count != sr.count, f"Expected 4 staging registers, got {sr_count}") 2297ec681f3Smrg 2307ec681f3Smrg encoded |= ((sr.encoded_flags | base) << sr.start) 2317ec681f3Smrg operands = operands[len(ins.staging):] 2327ec681f3Smrg 2337ec681f3Smrg for op, dest in zip(operands, ins.dests): 2347ec681f3Smrg encoded |= encode_dest(op) << 40 2357ec681f3Smrg operands = operands[len(ins.dests):] 2367ec681f3Smrg 2377ec681f3Smrg if len(ins.dests) == 0 and len(ins.staging) == 0: 2387ec681f3Smrg # Set a placeholder writemask to prevent encoding faults 2397ec681f3Smrg encoded |= (0xC0 << 40) 2407ec681f3Smrg 2417ec681f3Smrg fau = FAUState(immediate_mode) 2427ec681f3Smrg 2437ec681f3Smrg for i, (op, src) in enumerate(zip(operands, ins.srcs)): 2447ec681f3Smrg parts = op.split('.') 2457ec681f3Smrg encoded |= encode_source(parts[0], fau) << (i * 8) 2467ec681f3Smrg 2477ec681f3Smrg # Has a swizzle been applied yet? 2487ec681f3Smrg swizzled = False 2497ec681f3Smrg 2507ec681f3Smrg for mod in parts[1:]: 2517ec681f3Smrg # Encode the modifier 2527ec681f3Smrg if mod in src.offset and src.bits[mod] == 1: 2537ec681f3Smrg encoded |= (1 << src.offset[mod]) 2547ec681f3Smrg elif mod in enums[f'swizzles_{src.size}_bit'].bare_values and (src.widen or src.lanes): 2557ec681f3Smrg die_if(swizzled, "Multiple swizzles specified") 2567ec681f3Smrg swizzled = True 2577ec681f3Smrg val = enums[f'swizzles_{src.size}_bit'].bare_values.index(mod) 2587ec681f3Smrg encoded |= (val << src.offset['widen']) 2597ec681f3Smrg elif src.lane and mod in enums[f'lane_{src.size}_bit'].bare_values: 2607ec681f3Smrg die_if(swizzled, "Multiple swizzles specified") 2617ec681f3Smrg swizzled = True 2627ec681f3Smrg val = enums[f'lane_{src.size}_bit'].bare_values.index(mod) 2637ec681f3Smrg encoded |= (val << src.offset['lane']) 2647ec681f3Smrg elif src.size == 32 and mod in enums['widen'].bare_values: 2657ec681f3Smrg die_if(not src.swizzle, "Instruction doesn't take widens") 2667ec681f3Smrg die_if(swizzled, "Multiple swizzles specified") 2677ec681f3Smrg swizzled = True 2687ec681f3Smrg val = enums['widen'].bare_values.index(mod) 2697ec681f3Smrg encoded |= (val << src.offset['swizzle']) 2707ec681f3Smrg elif src.size == 16 and mod in enums['swizzles_16_bit'].bare_values: 2717ec681f3Smrg die_if(not src.swizzle, "Instruction doesn't take swizzles") 2727ec681f3Smrg die_if(swizzled, "Multiple swizzles specified") 2737ec681f3Smrg swizzled = True 2747ec681f3Smrg val = enums['swizzles_16_bit'].bare_values.index(mod) 2757ec681f3Smrg encoded |= (val << src.offset['swizzle']) 2767ec681f3Smrg elif mod in enums['lane_8_bit'].bare_values: 2777ec681f3Smrg die_if(not src.lane, "Instruction doesn't take a lane") 2787ec681f3Smrg die_if(swizzled, "Multiple swizzles specified") 2797ec681f3Smrg swizzled = True 2807ec681f3Smrg val = enums['lane_8_bit'].bare_values.index(mod) 2817ec681f3Smrg encoded |= (val << src.lane) 2827ec681f3Smrg else: 2837ec681f3Smrg die(f"Unknown modifier {mod}") 2847ec681f3Smrg 2857ec681f3Smrg # Encode the identity if a swizzle is required but not specified 2867ec681f3Smrg if src.swizzle and not swizzled and src.size == 16: 2877ec681f3Smrg mod = enums['swizzles_16_bit'].default 2887ec681f3Smrg val = enums['swizzles_16_bit'].bare_values.index(mod) 2897ec681f3Smrg encoded |= (val << src.offset['swizzle']) 2907ec681f3Smrg elif src.widen and not swizzled and src.size == 16: 2917ec681f3Smrg die_if(swizzled, "Multiple swizzles specified") 2927ec681f3Smrg mod = enums['swizzles_16_bit'].default 2937ec681f3Smrg val = enums['swizzles_16_bit'].bare_values.index(mod) 2947ec681f3Smrg encoded |= (val << src.offset['widen']) 2957ec681f3Smrg 2967ec681f3Smrg operands = operands[len(ins.srcs):] 2977ec681f3Smrg 2987ec681f3Smrg for i, (op, imm) in enumerate(zip(operands, ins.immediates)): 2997ec681f3Smrg if op[0] == '#': 3007ec681f3Smrg die_if(imm.name != 'constant', "Wrong syntax for immediate") 3017ec681f3Smrg parts = [imm.name, op[1:]] 3027ec681f3Smrg else: 3037ec681f3Smrg parts = op.split(':') 3047ec681f3Smrg die_if(len(parts) != 2, f"Wrong syntax for immediate, wrong number of colons in {op}") 3057ec681f3Smrg die_if(parts[0] != imm.name, f"Wrong immediate, expected {imm.name}, got {parts[0]}") 3067ec681f3Smrg 3077ec681f3Smrg if imm.signed: 3087ec681f3Smrg minimum = -(1 << (imm.size - 1)) 3097ec681f3Smrg maximum = +(1 << (imm.size - 1)) - 1 3107ec681f3Smrg else: 3117ec681f3Smrg minimum = 0 3127ec681f3Smrg maximum = (1 << imm.size) - 1 3137ec681f3Smrg 3147ec681f3Smrg val = parse_int(parts[1], minimum, maximum) 3157ec681f3Smrg 3167ec681f3Smrg if val < 0: 3177ec681f3Smrg # Sign extends 3187ec681f3Smrg val = (1 << imm.size) + val 3197ec681f3Smrg 3207ec681f3Smrg encoded |= (val << imm.start) 3217ec681f3Smrg 3227ec681f3Smrg operands = operands[len(ins.immediates):] 3237ec681f3Smrg 3247ec681f3Smrg # Encode the operation itself 3257ec681f3Smrg encoded |= (ins.opcode << 48) 3267ec681f3Smrg encoded |= (ins.opcode2 << ins.secondary_shift) 3277ec681f3Smrg 3287ec681f3Smrg # Encode modifiers 3297ec681f3Smrg has_action = False 3307ec681f3Smrg for mod in mods: 3317ec681f3Smrg if len(mod) == 0: 3327ec681f3Smrg continue 3337ec681f3Smrg 3347ec681f3Smrg if mod in enums['action'].bare_values: 3357ec681f3Smrg die_if(has_action, "Multiple actions specified") 3367ec681f3Smrg has_action = True 3377ec681f3Smrg encoded |= (enums['action'].bare_values.index(mod) << 59) 3387ec681f3Smrg encoded |= (1 << 62) # Action, not wait 3397ec681f3Smrg elif mod.startswith('wait'): 3407ec681f3Smrg die_if(has_action, "Multiple actions specified") 3417ec681f3Smrg has_action = True 3427ec681f3Smrg 3437ec681f3Smrg slots = mod[len('wait'):] 3447ec681f3Smrg try: 3457ec681f3Smrg slots = set([int(x) for x in slots]) 3467ec681f3Smrg except ValueError: 3477ec681f3Smrg die(f"Expected slots in {mod}") 3487ec681f3Smrg 3497ec681f3Smrg known_slots = set([0, 1, 2]) 3507ec681f3Smrg die_if(not slots.issubset(known_slots), f"Unknown slots in {mod}") 3517ec681f3Smrg 3527ec681f3Smrg if 0 in slots: 3537ec681f3Smrg encoded |= (1 << 59) 3547ec681f3Smrg if 1 in slots: 3557ec681f3Smrg encoded |= (1 << 60) 3567ec681f3Smrg if 2 in slots: 3577ec681f3Smrg encoded |= (1 << 61) 3587ec681f3Smrg elif mod in enums['immediate_mode'].bare_values: 3597ec681f3Smrg pass # handled specially 3607ec681f3Smrg else: 3617ec681f3Smrg candidates = [c for c in ins.modifiers if mod in c.bare_values] 3627ec681f3Smrg 3637ec681f3Smrg die_if(len(candidates) == 0, f"Invalid modifier {mod} used") 3647ec681f3Smrg assert(len(candidates) == 1) # No ambiguous modifiers 3657ec681f3Smrg opts = candidates[0] 3667ec681f3Smrg 3677ec681f3Smrg value = opts.bare_values.index(mod) 3687ec681f3Smrg assert(value is not None) 3697ec681f3Smrg 3707ec681f3Smrg die_if(opts.name in modifier_map, f"{opts.name} specified twice") 3717ec681f3Smrg modifier_map[opts.name] = value 3727ec681f3Smrg 3737ec681f3Smrg for mod in ins.modifiers: 3747ec681f3Smrg value = modifier_map.get(mod.name, mod.default) 3757ec681f3Smrg die_if(value is None, f"Missing required modifier {mod.name}") 3767ec681f3Smrg 3777ec681f3Smrg assert(value < (1 << mod.size)) 3787ec681f3Smrg encoded |= (value << mod.start) 3797ec681f3Smrg 3807ec681f3Smrg encoded |= (enums['immediate_mode'].bare_values.index(immediate_mode) << 57) 3817ec681f3Smrg return encoded 3827ec681f3Smrg 3837ec681f3Smrgif __name__ == "__main__": 3847ec681f3Smrg # Provide commandline interface 3857ec681f3Smrg parser = argparse.ArgumentParser(description='Assemble Valhall shaders') 3867ec681f3Smrg parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), 3877ec681f3Smrg default=sys.stdin) 3887ec681f3Smrg parser.add_argument('outfile', type=argparse.FileType('wb')) 3897ec681f3Smrg args = parser.parse_args() 3907ec681f3Smrg 3917ec681f3Smrg lines = args.infile.read().strip().split('\n') 3927ec681f3Smrg lines = [l for l in lines if len(l) > 0 and l[0] != '#'] 3937ec681f3Smrg 3947ec681f3Smrg packed = b''.join([struct.pack('<Q', parse_asm(ln)) for ln in lines]) 3957ec681f3Smrg args.outfile.write(packed) 396