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