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