1bf215546Sopenharmony_ci#
2bf215546Sopenharmony_ci# Copyright (C) 2020 Collabora, Ltd.
3bf215546Sopenharmony_ci#
4bf215546Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci# copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci# to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci# the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci# and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci# Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci#
11bf215546Sopenharmony_ci# The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci# Software.
14bf215546Sopenharmony_ci#
15bf215546Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21bf215546Sopenharmony_ci# IN THE SOFTWARE.
22bf215546Sopenharmony_ci
23bf215546Sopenharmony_ciimport sys
24bf215546Sopenharmony_cifrom bifrost_isa import *
25bf215546Sopenharmony_cifrom mako.template import Template
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_ci# Consider pseudo instructions when getting the modifier list
28bf215546Sopenharmony_ciinstructions_with_pseudo = parse_instructions(sys.argv[1], include_pseudo = True)
29bf215546Sopenharmony_ciir_instructions_with_pseudo = partition_mnemonics(instructions_with_pseudo)
30bf215546Sopenharmony_cimodifier_lists = order_modifiers(ir_instructions_with_pseudo)
31bf215546Sopenharmony_ci
32bf215546Sopenharmony_ci# ...but strip for packing
33bf215546Sopenharmony_ciinstructions = parse_instructions(sys.argv[1])
34bf215546Sopenharmony_ciir_instructions = partition_mnemonics(instructions)
35bf215546Sopenharmony_ci
36bf215546Sopenharmony_ci# Packs sources into an argument. Offset argument to work around a quirk of our
37bf215546Sopenharmony_ci# compiler IR when dealing with staging registers (TODO: reorder in the IR to
38bf215546Sopenharmony_ci# fix this)
39bf215546Sopenharmony_cidef pack_sources(sources, body, pack_exprs, offset, is_fma):
40bf215546Sopenharmony_ci    for i, src in enumerate(sources):
41bf215546Sopenharmony_ci        # FMA first two args are restricted, but that's checked once for all
42bf215546Sopenharmony_ci        # FMA so the compiler has less work to do
43bf215546Sopenharmony_ci        expected = 0xFB if (is_fma and i < 2) else 0xFF
44bf215546Sopenharmony_ci
45bf215546Sopenharmony_ci        # Validate the source
46bf215546Sopenharmony_ci        if src[1] != expected:
47bf215546Sopenharmony_ci            assert((src[1] & expected) == src[1])
48bf215546Sopenharmony_ci            body.append('assert((1 << src{}) & {});'.format(i, hex(src[1])))
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci        # Sources are state-invariant
51bf215546Sopenharmony_ci        for state in pack_exprs:
52bf215546Sopenharmony_ci            state.append('(src{} << {})'.format(i, src[0]))
53bf215546Sopenharmony_ci
54bf215546Sopenharmony_ci# Try to map from a modifier list `domain` to the list `target`
55bf215546Sopenharmony_cidef map_modifier(body, prefix, mod, domain, target):
56bf215546Sopenharmony_ci    # We don't want to map reserveds, that's invalid IR anyway
57bf215546Sopenharmony_ci    def reserved_to_none(arr):
58bf215546Sopenharmony_ci        return [None if x == 'reserved' else x for x in arr]
59bf215546Sopenharmony_ci
60bf215546Sopenharmony_ci    # Trim out reserveds at the end
61bf215546Sopenharmony_ci    noned_domain = reserved_to_none(domain)
62bf215546Sopenharmony_ci    noned_target = reserved_to_none(target)
63bf215546Sopenharmony_ci    none_indices = [i for i, x in enumerate(noned_target) if x != None]
64bf215546Sopenharmony_ci    trimmed = noned_target[0: none_indices[-1] + 1]
65bf215546Sopenharmony_ci
66bf215546Sopenharmony_ci    if trimmed == noned_domain[0:len(trimmed)]:
67bf215546Sopenharmony_ci        # Identity map, possibly on the left subset
68bf215546Sopenharmony_ci        return mod
69bf215546Sopenharmony_ci    else:
70bf215546Sopenharmony_ci        # Generate a table as a fallback
71bf215546Sopenharmony_ci        table = ", ".join([str(target.index(x)) if x in target else "~0" for x in domain])
72bf215546Sopenharmony_ci        body.append("static uint8_t {}_table[] = {{ {} }};".format(prefix, table))
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_ci        if len(domain) > 2:
75bf215546Sopenharmony_ci            # no need to validate bools
76bf215546Sopenharmony_ci            body.append("assert({} < {});".format(mod, len(domain)))
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_ci        return "{}_table[{}]".format(prefix, mod)
79bf215546Sopenharmony_ci
80bf215546Sopenharmony_cidef pick_from_bucket(opts, bucket):
81bf215546Sopenharmony_ci    intersection = set(opts) & bucket
82bf215546Sopenharmony_ci    assert(len(intersection) <= 1)
83bf215546Sopenharmony_ci    return intersection.pop() if len(intersection) == 1 else None
84bf215546Sopenharmony_ci
85bf215546Sopenharmony_cidef pack_modifier(mod, width, default, opts, body, pack_exprs):
86bf215546Sopenharmony_ci    # Destructure the modifier name
87bf215546Sopenharmony_ci    (raw, arg) = (mod[0:-1], mod[-1]) if mod[-1] in "0123" else (mod, 0)
88bf215546Sopenharmony_ci
89bf215546Sopenharmony_ci    SWIZZLES = ["lane", "lanes", "replicate", "swz", "widen", "swap"]
90bf215546Sopenharmony_ci
91bf215546Sopenharmony_ci    ir_value = "bytes2" if mod == "bytes2" else "{}[{}]".format(raw, arg) if mod[-1] in "0123" else mod
92bf215546Sopenharmony_ci    lists = modifier_lists[raw]
93bf215546Sopenharmony_ci
94bf215546Sopenharmony_ci    # Swizzles need to be packed "specially"
95bf215546Sopenharmony_ci    SWIZZLE_BUCKETS = [
96bf215546Sopenharmony_ci            set(['h00', 'h0']),
97bf215546Sopenharmony_ci            set(['h01', 'none', 'b0123', 'w0']), # Identity
98bf215546Sopenharmony_ci            set(['h10']),
99bf215546Sopenharmony_ci            set(['h11', 'h1']),
100bf215546Sopenharmony_ci            set(['b0000', 'b00', 'b0']),
101bf215546Sopenharmony_ci            set(['b1111', 'b11', 'b1']),
102bf215546Sopenharmony_ci            set(['b2222', 'b22', 'b2']),
103bf215546Sopenharmony_ci            set(['b3333', 'b33', 'b3']),
104bf215546Sopenharmony_ci            set(['b0011', 'b01']),
105bf215546Sopenharmony_ci            set(['b2233', 'b23']),
106bf215546Sopenharmony_ci            set(['b1032']),
107bf215546Sopenharmony_ci            set(['b3210']),
108bf215546Sopenharmony_ci            set(['b0022', 'b02'])
109bf215546Sopenharmony_ci    ]
110bf215546Sopenharmony_ci
111bf215546Sopenharmony_ci    if raw in SWIZZLES:
112bf215546Sopenharmony_ci        # Construct a list
113bf215546Sopenharmony_ci        lists = [pick_from_bucket(opts, bucket) for bucket in SWIZZLE_BUCKETS]
114bf215546Sopenharmony_ci        ir_value = "src[{}].swizzle".format(arg)
115bf215546Sopenharmony_ci    elif raw == "lane_dest":
116bf215546Sopenharmony_ci        lists = [pick_from_bucket(opts, bucket) for bucket in SWIZZLE_BUCKETS]
117bf215546Sopenharmony_ci        ir_value = "dest->swizzle"
118bf215546Sopenharmony_ci    elif raw in ["abs", "sign"]:
119bf215546Sopenharmony_ci        ir_value = "src[{}].abs".format(arg)
120bf215546Sopenharmony_ci    elif raw in ["neg", "not"]:
121bf215546Sopenharmony_ci        ir_value = "src[{}].neg".format(arg)
122bf215546Sopenharmony_ci
123bf215546Sopenharmony_ci    ir_value = "I->{}".format(ir_value)
124bf215546Sopenharmony_ci
125bf215546Sopenharmony_ci    # We need to map from ir_opts to opts
126bf215546Sopenharmony_ci    mapped = map_modifier(body, mod, ir_value, lists, opts)
127bf215546Sopenharmony_ci    body.append('unsigned {} = {};'.format(mod, mapped))
128bf215546Sopenharmony_ci    body.append('assert({} < {});'.format(mod, 1 << width))
129bf215546Sopenharmony_ci
130bf215546Sopenharmony_ci# Compiles an S-expression (and/or/eq/neq, modifiers, `ordering`, immediates)
131bf215546Sopenharmony_ci# into a C boolean expression suitable to stick in an if-statement. Takes an
132bf215546Sopenharmony_ci# imm_map to map modifiers to immediate values, parametrized by the ctx that
133bf215546Sopenharmony_ci# we're looking up in (the first, non-immediate argument of the equality)
134bf215546Sopenharmony_ci
135bf215546Sopenharmony_ciSEXPR_BINARY = {
136bf215546Sopenharmony_ci        "and": "&&",
137bf215546Sopenharmony_ci        "or": "||",
138bf215546Sopenharmony_ci        "eq": "==",
139bf215546Sopenharmony_ci        "neq": "!="
140bf215546Sopenharmony_ci}
141bf215546Sopenharmony_ci
142bf215546Sopenharmony_cidef compile_s_expr(expr, imm_map, ctx):
143bf215546Sopenharmony_ci    if expr[0] == 'alias':
144bf215546Sopenharmony_ci        return compile_s_expr(expr[1], imm_map, ctx)
145bf215546Sopenharmony_ci    elif expr == ['eq', 'ordering', '#gt']:
146bf215546Sopenharmony_ci        return '(src0 > src1)'
147bf215546Sopenharmony_ci    elif expr == ['neq', 'ordering', '#lt']:
148bf215546Sopenharmony_ci        return '(src0 >= src1)'
149bf215546Sopenharmony_ci    elif expr == ['neq', 'ordering', '#gt']:
150bf215546Sopenharmony_ci        return '(src0 <= src1)'
151bf215546Sopenharmony_ci    elif expr == ['eq', 'ordering', '#lt']:
152bf215546Sopenharmony_ci        return '(src0 < src1)'
153bf215546Sopenharmony_ci    elif expr == ['eq', 'ordering', '#eq']:
154bf215546Sopenharmony_ci        return '(src0 == src1)'
155bf215546Sopenharmony_ci    elif isinstance(expr, list):
156bf215546Sopenharmony_ci        sep = " {} ".format(SEXPR_BINARY[expr[0]])
157bf215546Sopenharmony_ci        return "(" + sep.join([compile_s_expr(s, imm_map, expr[1]) for s in expr[1:]]) + ")"
158bf215546Sopenharmony_ci    elif expr[0] == '#':
159bf215546Sopenharmony_ci        return str(imm_map[ctx][expr[1:]])
160bf215546Sopenharmony_ci    else:
161bf215546Sopenharmony_ci        return expr
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci# Packs a derived value. We just iterate through the possible choices and test
164bf215546Sopenharmony_ci# whether the encoding matches, and if so we use it.
165bf215546Sopenharmony_ci
166bf215546Sopenharmony_cidef pack_derived(pos, exprs, imm_map, body, pack_exprs):
167bf215546Sopenharmony_ci    body.append('unsigned derived_{} = 0;'.format(pos))
168bf215546Sopenharmony_ci
169bf215546Sopenharmony_ci    first = True
170bf215546Sopenharmony_ci    for i, expr in enumerate(exprs):
171bf215546Sopenharmony_ci        if expr is not None:
172bf215546Sopenharmony_ci            cond = compile_s_expr(expr, imm_map, None)
173bf215546Sopenharmony_ci            body.append('{}if {} derived_{} = {};'.format('' if first else 'else ', cond, pos, i))
174bf215546Sopenharmony_ci            first = False
175bf215546Sopenharmony_ci
176bf215546Sopenharmony_ci    assert (not first)
177bf215546Sopenharmony_ci    body.append('else unreachable("No pattern match at pos {}");'.format(pos))
178bf215546Sopenharmony_ci    body.append('')
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci    assert(pos is not None)
181bf215546Sopenharmony_ci    pack_exprs.append('(derived_{} << {})'.format(pos, pos))
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci# Generates a routine to pack a single variant of a single- instruction.
184bf215546Sopenharmony_ci# Template applies the needed formatting and combine to OR together all the
185bf215546Sopenharmony_ci# pack_exprs to avoid bit fields.
186bf215546Sopenharmony_ci#
187bf215546Sopenharmony_ci# Argument swapping is sensitive to the order of operations. Dependencies:
188bf215546Sopenharmony_ci# sources (RW), modifiers (RW), derived values (W). Hence we emit sources and
189bf215546Sopenharmony_ci# modifiers first, then perform a swap if necessary overwriting
190bf215546Sopenharmony_ci# sources/modifiers, and last calculate derived values and pack.
191bf215546Sopenharmony_ci
192bf215546Sopenharmony_civariant_template = Template("""static inline unsigned
193bf215546Sopenharmony_cibi_pack_${name}(${", ".join(["bi_instr *I"] + ["enum bifrost_packed_src src{}".format(i) for i in range(srcs)])})
194bf215546Sopenharmony_ci{
195bf215546Sopenharmony_ci${"\\n".join([("    " + x) for x in common_body])}
196bf215546Sopenharmony_ci% if single_state:
197bf215546Sopenharmony_ci% for (pack_exprs, s_body, _) in states:
198bf215546Sopenharmony_ci${"\\n".join(["    " + x for x in s_body + ["return {};".format( " | ".join(pack_exprs))]])}
199bf215546Sopenharmony_ci% endfor
200bf215546Sopenharmony_ci% else:
201bf215546Sopenharmony_ci% for i, (pack_exprs, s_body, cond) in enumerate(states):
202bf215546Sopenharmony_ci    ${'} else ' if i > 0 else ''}if ${cond} {
203bf215546Sopenharmony_ci${"\\n".join(["        " + x for x in s_body + ["return {};".format(" | ".join(pack_exprs))]])}
204bf215546Sopenharmony_ci% endfor
205bf215546Sopenharmony_ci    } else {
206bf215546Sopenharmony_ci        unreachable("No matching state found in ${name}");
207bf215546Sopenharmony_ci    }
208bf215546Sopenharmony_ci% endif
209bf215546Sopenharmony_ci}
210bf215546Sopenharmony_ci""")
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_cidef pack_variant(opname, states):
213bf215546Sopenharmony_ci    # Expressions to be ORed together for the final pack, an array per state
214bf215546Sopenharmony_ci    pack_exprs = [[hex(state[1]["exact"][1])] for state in states]
215bf215546Sopenharmony_ci
216bf215546Sopenharmony_ci    # Computations which need to be done to encode first, across states
217bf215546Sopenharmony_ci    common_body = []
218bf215546Sopenharmony_ci
219bf215546Sopenharmony_ci    # Map from modifier names to a map from modifier values to encoded values
220bf215546Sopenharmony_ci    # String -> { String -> Uint }. This can be shared across states since
221bf215546Sopenharmony_ci    # modifiers are (except the pos values) constant across state.
222bf215546Sopenharmony_ci    imm_map = {}
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_ci    # Pack sources. Offset over to deal with staging/immediate weirdness in our
225bf215546Sopenharmony_ci    # IR (TODO: reorder sources upstream so this goes away). Note sources are
226bf215546Sopenharmony_ci    # constant across states.
227bf215546Sopenharmony_ci    staging = states[0][1].get("staging", "")
228bf215546Sopenharmony_ci    offset = 0
229bf215546Sopenharmony_ci    if staging in ["r", "rw"]:
230bf215546Sopenharmony_ci        offset += 1
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ci    pack_sources(states[0][1].get("srcs", []), common_body, pack_exprs, offset, opname[0] == '*')
233bf215546Sopenharmony_ci
234bf215546Sopenharmony_ci    modifiers_handled = []
235bf215546Sopenharmony_ci    for st in states:
236bf215546Sopenharmony_ci        for ((mod, _, width), default, opts) in st[1].get("modifiers", []):
237bf215546Sopenharmony_ci            if mod in modifiers_handled:
238bf215546Sopenharmony_ci                continue
239bf215546Sopenharmony_ci
240bf215546Sopenharmony_ci            modifiers_handled.append(mod)
241bf215546Sopenharmony_ci            pack_modifier(mod, width, default, opts, common_body, pack_exprs)
242bf215546Sopenharmony_ci
243bf215546Sopenharmony_ci            imm_map[mod] = { x: y for y, x in enumerate(opts) }
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_ci    for i, st in enumerate(states):
246bf215546Sopenharmony_ci        for ((mod, pos, width), default, opts) in st[1].get("modifiers", []):
247bf215546Sopenharmony_ci            if pos is not None:
248bf215546Sopenharmony_ci                pack_exprs[i].append('({} << {})'.format(mod, pos))
249bf215546Sopenharmony_ci
250bf215546Sopenharmony_ci    for ((src_a, src_b), cond, remap) in st[1].get("swaps", []):
251bf215546Sopenharmony_ci        # Figure out which vars to swap, in order to swap the arguments. This
252bf215546Sopenharmony_ci        # always includes the sources themselves, and may include source
253bf215546Sopenharmony_ci        # modifiers (with the same source indices). We swap based on which
254bf215546Sopenharmony_ci        # matches A, this is arbitrary but if we swapped both nothing would end
255bf215546Sopenharmony_ci        # up swapping at all since it would swap back.
256bf215546Sopenharmony_ci
257bf215546Sopenharmony_ci        vars_to_swap = ['src']
258bf215546Sopenharmony_ci        for ((mod, _, width), default, opts) in st[1].get("modifiers", []):
259bf215546Sopenharmony_ci            if mod[-1] in str(src_a):
260bf215546Sopenharmony_ci                vars_to_swap.append(mod[0:-1])
261bf215546Sopenharmony_ci
262bf215546Sopenharmony_ci        common_body.append('if {}'.format(compile_s_expr(cond, imm_map, None)) + ' {')
263bf215546Sopenharmony_ci
264bf215546Sopenharmony_ci        # Emit the swaps. We use a temp, and wrap in a block to avoid naming
265bf215546Sopenharmony_ci        # collisions with multiple swaps. {{Doubling}} to escape the format.
266bf215546Sopenharmony_ci
267bf215546Sopenharmony_ci        for v in vars_to_swap:
268bf215546Sopenharmony_ci            common_body.append('    {{ unsigned temp = {}{}; {}{} = {}{}; {}{} = temp; }}'.format(v, src_a, v, src_a, v, src_b, v, src_b))
269bf215546Sopenharmony_ci
270bf215546Sopenharmony_ci        # Also, remap. Bidrectional swaps are explicit in the XML.
271bf215546Sopenharmony_ci        for v in remap:
272bf215546Sopenharmony_ci            maps = remap[v]
273bf215546Sopenharmony_ci            imm = imm_map[v]
274bf215546Sopenharmony_ci
275bf215546Sopenharmony_ci            for i, l in enumerate(maps):
276bf215546Sopenharmony_ci                common_body.append('    {}if ({} == {}) {} = {};'.format('' if i == 0 else 'else ', v, imm[l], v, imm[maps[l]]))
277bf215546Sopenharmony_ci
278bf215546Sopenharmony_ci        common_body.append('}')
279bf215546Sopenharmony_ci        common_body.append('')
280bf215546Sopenharmony_ci
281bf215546Sopenharmony_ci    for (name, pos, width) in st[1].get("immediates", []):
282bf215546Sopenharmony_ci        common_body.append('unsigned {} = I->{};'.format(name, name))
283bf215546Sopenharmony_ci        common_body.append('assert({} < {});'.format(name, hex(1 << width)))
284bf215546Sopenharmony_ci
285bf215546Sopenharmony_ci        for st in pack_exprs:
286bf215546Sopenharmony_ci            st.append('({} << {})'.format(name, pos))
287bf215546Sopenharmony_ci
288bf215546Sopenharmony_ci    # After this, we have to branch off, since deriveds *do* vary based on state.
289bf215546Sopenharmony_ci    state_body = [[] for s in states]
290bf215546Sopenharmony_ci
291bf215546Sopenharmony_ci    for i, (_, st) in enumerate(states):
292bf215546Sopenharmony_ci        for ((pos, width), exprs) in st.get("derived", []):
293bf215546Sopenharmony_ci            pack_derived(pos, exprs, imm_map, state_body[i], pack_exprs[i])
294bf215546Sopenharmony_ci
295bf215546Sopenharmony_ci    # How do we pick a state? Accumulate the conditions
296bf215546Sopenharmony_ci    state_conds = [compile_s_expr(st[0], imm_map, None) for st in states] if len(states) > 1 else [None]
297bf215546Sopenharmony_ci
298bf215546Sopenharmony_ci    if state_conds == None:
299bf215546Sopenharmony_ci        assert (states[0][0] == None)
300bf215546Sopenharmony_ci
301bf215546Sopenharmony_ci    # Finally, we'll collect everything together
302bf215546Sopenharmony_ci    return variant_template.render(name = opname_to_c(opname), states = zip(pack_exprs, state_body, state_conds), common_body = common_body, single_state = (len(states) == 1), srcs = 4)
303bf215546Sopenharmony_ci
304bf215546Sopenharmony_ciprint(COPYRIGHT + '#include "compiler.h"')
305bf215546Sopenharmony_ci
306bf215546Sopenharmony_cipacks = [pack_variant(e, instructions[e]) for e in instructions]
307bf215546Sopenharmony_cifor p in packs:
308bf215546Sopenharmony_ci    print(p)
309bf215546Sopenharmony_ci
310bf215546Sopenharmony_citop_pack = Template("""unsigned
311bf215546Sopenharmony_cibi_pack_${'fma' if unit == '*' else 'add'}(bi_instr *I,
312bf215546Sopenharmony_ci    enum bifrost_packed_src src0,
313bf215546Sopenharmony_ci    enum bifrost_packed_src src1,
314bf215546Sopenharmony_ci    enum bifrost_packed_src src2,
315bf215546Sopenharmony_ci    enum bifrost_packed_src src3)
316bf215546Sopenharmony_ci{
317bf215546Sopenharmony_ci    if (!I)
318bf215546Sopenharmony_ci        return bi_pack_${opname_to_c(unit + 'NOP')}(I, src0, src1, src2, src3);
319bf215546Sopenharmony_ci
320bf215546Sopenharmony_ci% if unit == '*':
321bf215546Sopenharmony_ci    assert((1 << src0) & 0xfb);
322bf215546Sopenharmony_ci    assert((1 << src1) & 0xfb);
323bf215546Sopenharmony_ci
324bf215546Sopenharmony_ci% endif
325bf215546Sopenharmony_ci    switch (I->op) {
326bf215546Sopenharmony_ci% for opcode in ops:
327bf215546Sopenharmony_ci% if unit + opcode in instructions:
328bf215546Sopenharmony_ci    case BI_OPCODE_${opcode.replace('.', '_').upper()}:
329bf215546Sopenharmony_ci        return bi_pack_${opname_to_c(unit + opcode)}(I, src0, src1, src2, src3);
330bf215546Sopenharmony_ci% endif
331bf215546Sopenharmony_ci% endfor
332bf215546Sopenharmony_ci    default:
333bf215546Sopenharmony_ci#ifndef NDEBUG
334bf215546Sopenharmony_ci        bi_print_instr(I, stderr);
335bf215546Sopenharmony_ci#endif
336bf215546Sopenharmony_ci        unreachable("Cannot pack instruction as ${unit}");
337bf215546Sopenharmony_ci    }
338bf215546Sopenharmony_ci}
339bf215546Sopenharmony_ci""")
340bf215546Sopenharmony_ci
341bf215546Sopenharmony_cifor unit in ['*', '+']:
342bf215546Sopenharmony_ci    print(top_pack.render(ops = ir_instructions, instructions = instructions, opname_to_c = opname_to_c, unit = unit))
343