Path: blob/21.2-virgl/src/panfrost/bifrost/bi_packer.c.py
4564 views
#1# Copyright (C) 2020 Collabora, Ltd.2#3# Permission is hereby granted, free of charge, to any person obtaining a4# copy of this software and associated documentation files (the "Software"),5# to deal in the Software without restriction, including without limitation6# the rights to use, copy, modify, merge, publish, distribute, sublicense,7# and/or sell copies of the Software, and to permit persons to whom the8# Software is furnished to do so, subject to the following conditions:9#10# The above copyright notice and this permission notice (including the next11# paragraph) shall be included in all copies or substantial portions of the12# Software.13#14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20# IN THE SOFTWARE.2122import sys23from bifrost_isa import *24from mako.template import Template2526instructions = parse_instructions(sys.argv[1])27ir_instructions = partition_mnemonics(instructions)28modifier_lists = order_modifiers(ir_instructions)2930# Packs sources into an argument. Offset argument to work around a quirk of our31# compiler IR when dealing with staging registers (TODO: reorder in the IR to32# fix this)33def pack_sources(sources, body, pack_exprs, offset, is_fma):34for i, src in enumerate(sources):35# FMA first two args are restricted, but that's checked once for all36# FMA so the compiler has less work to do37expected = 0xFB if (is_fma and i < 2) else 0xFF3839# Validate the source40if src[1] != expected:41assert((src[1] & expected) == src[1])42body.append('assert((1 << src{}) & {});'.format(i, hex(src[1])))4344# Sources are state-invariant45for state in pack_exprs:46state.append('(src{} << {})'.format(i, src[0]))4748# Try to map from a modifier list `domain` to the list `target`49def map_modifier(body, prefix, mod, domain, target):50# We don't want to map reserveds, that's invalid IR anyway51def reserved_to_none(arr):52return [None if x == 'reserved' else x for x in arr]5354# Trim out reserveds at the end55noned_domain = reserved_to_none(domain)56noned_target = reserved_to_none(target)57none_indices = [i for i, x in enumerate(noned_target) if x != None]58trimmed = noned_target[0: none_indices[-1] + 1]5960if trimmed == noned_domain[0:len(trimmed)]:61# Identity map, possibly on the left subset62return mod63else:64# Generate a table as a fallback65table = ", ".join([str(target.index(x)) if x in target else "~0" for x in domain])66body.append("static uint8_t {}_table[] = {{ {} }};".format(prefix, table))6768if len(domain) > 2:69# no need to validate bools70body.append("assert({} < {});".format(mod, len(domain)))7172return "{}_table[{}]".format(prefix, mod)7374def pick_from_bucket(opts, bucket):75intersection = set(opts) & bucket76assert(len(intersection) <= 1)77return intersection.pop() if len(intersection) == 1 else None7879def pack_modifier(mod, width, default, opts, body, pack_exprs):80# Destructure the modifier name81(raw, arg) = (mod[0:-1], mod[-1]) if mod[-1] in "0123" else (mod, 0)8283SWIZZLES = ["lane", "lanes", "replicate", "swz", "widen", "swap"]8485ir_value = "bytes2" if mod == "bytes2" else "{}[{}]".format(raw, arg) if mod[-1] in "0123" else mod86lists = modifier_lists[raw]8788# Swizzles need to be packed "specially"89SWIZZLE_BUCKETS = [90set(['h00', 'h0']),91set(['h01', 'none', 'b0123', 'w0']), # Identity92set(['h10']),93set(['h11', 'h1']),94set(['b0000', 'b00', 'b0']),95set(['b1111', 'b11', 'b1']),96set(['b2222', 'b22', 'b2']),97set(['b3333', 'b33', 'b3']),98set(['b0011', 'b01']),99set(['b2233', 'b23']),100set(['b1032']),101set(['b3210']),102set(['b0022', 'b02'])103]104105if raw in SWIZZLES:106# Construct a list107lists = [pick_from_bucket(opts, bucket) for bucket in SWIZZLE_BUCKETS]108ir_value = "src[{}].swizzle".format(arg)109elif raw == "lane_dest":110lists = [pick_from_bucket(opts, bucket) for bucket in SWIZZLE_BUCKETS]111ir_value = "dest->swizzle"112elif raw in ["abs", "sign"]:113ir_value = "src[{}].abs".format(arg)114elif raw in ["neg", "not"]:115ir_value = "src[{}].neg".format(arg)116117ir_value = "I->{}".format(ir_value)118119# We need to map from ir_opts to opts120mapped = map_modifier(body, mod, ir_value, lists, opts)121body.append('unsigned {} = {};'.format(mod, mapped))122body.append('assert({} < {});'.format(mod, 1 << width))123124# Compiles an S-expression (and/or/eq/neq, modifiers, `ordering`, immediates)125# into a C boolean expression suitable to stick in an if-statement. Takes an126# imm_map to map modifiers to immediate values, parametrized by the ctx that127# we're looking up in (the first, non-immediate argument of the equality)128129SEXPR_BINARY = {130"and": "&&",131"or": "||",132"eq": "==",133"neq": "!="134}135136def compile_s_expr(expr, imm_map, ctx):137if expr[0] == 'alias':138return compile_s_expr(expr[1], imm_map, ctx)139elif expr == ['eq', 'ordering', '#gt']:140return '(src0 > src1)'141elif expr == ['neq', 'ordering', '#lt']:142return '(src0 >= src1)'143elif expr == ['neq', 'ordering', '#gt']:144return '(src0 <= src1)'145elif expr == ['eq', 'ordering', '#lt']:146return '(src0 < src1)'147elif expr == ['eq', 'ordering', '#eq']:148return '(src0 == src1)'149elif isinstance(expr, list):150sep = " {} ".format(SEXPR_BINARY[expr[0]])151return "(" + sep.join([compile_s_expr(s, imm_map, expr[1]) for s in expr[1:]]) + ")"152elif expr[0] == '#':153return str(imm_map[ctx][expr[1:]])154else:155return expr156157# Packs a derived value. We just iterate through the possible choices and test158# whether the encoding matches, and if so we use it.159160def pack_derived(pos, exprs, imm_map, body, pack_exprs):161body.append('unsigned derived_{} = 0;'.format(pos))162163first = True164for i, expr in enumerate(exprs):165if expr is not None:166cond = compile_s_expr(expr, imm_map, None)167body.append('{}if {} derived_{} = {};'.format('' if first else 'else ', cond, pos, i))168first = False169170assert (not first)171body.append('else unreachable("No pattern match at pos {}");'.format(pos))172body.append('')173174assert(pos is not None)175pack_exprs.append('(derived_{} << {})'.format(pos, pos))176177# Generates a routine to pack a single variant of a single- instruction.178# Template applies the needed formatting and combine to OR together all the179# pack_exprs to avoid bit fields.180#181# Argument swapping is sensitive to the order of operations. Dependencies:182# sources (RW), modifiers (RW), derived values (W). Hence we emit sources and183# modifiers first, then perform a swap if necessary overwriting184# sources/modifiers, and last calculate derived values and pack.185186variant_template = Template("""static inline unsigned187bi_pack_${name}(${", ".join(["bi_instr *I"] + ["enum bifrost_packed_src src{}".format(i) for i in range(srcs)])})188{189${"\\n".join([(" " + x) for x in common_body])}190% if single_state:191% for (pack_exprs, s_body, _) in states:192${"\\n".join([" " + x for x in s_body + ["return {};".format( " | ".join(pack_exprs))]])}193% endfor194% else:195% for i, (pack_exprs, s_body, cond) in enumerate(states):196${'} else ' if i > 0 else ''}if ${cond} {197${"\\n".join([" " + x for x in s_body + ["return {};".format(" | ".join(pack_exprs))]])}198% endfor199} else {200unreachable("No matching state found in ${name}");201}202% endif203}204""")205206def pack_variant(opname, states):207# Expressions to be ORed together for the final pack, an array per state208pack_exprs = [[hex(state[1]["exact"][1])] for state in states]209210# Computations which need to be done to encode first, across states211common_body = []212213# Map from modifier names to a map from modifier values to encoded values214# String -> { String -> Uint }. This can be shared across states since215# modifiers are (except the pos values) constant across state.216imm_map = {}217218# Pack sources. Offset over to deal with staging/immediate weirdness in our219# IR (TODO: reorder sources upstream so this goes away). Note sources are220# constant across states.221staging = states[0][1].get("staging", "")222offset = 0223if staging in ["r", "rw"]:224offset += 1225226pack_sources(states[0][1].get("srcs", []), common_body, pack_exprs, offset, opname[0] == '*')227228modifiers_handled = []229for st in states:230for ((mod, _, width), default, opts) in st[1].get("modifiers", []):231if mod in modifiers_handled:232continue233234modifiers_handled.append(mod)235pack_modifier(mod, width, default, opts, common_body, pack_exprs)236237imm_map[mod] = { x: y for y, x in enumerate(opts) }238239for i, st in enumerate(states):240for ((mod, pos, width), default, opts) in st[1].get("modifiers", []):241if pos is not None:242pack_exprs[i].append('({} << {})'.format(mod, pos))243244for ((src_a, src_b), cond, remap) in st[1].get("swaps", []):245# Figure out which vars to swap, in order to swap the arguments. This246# always includes the sources themselves, and may include source247# modifiers (with the same source indices). We swap based on which248# matches A, this is arbitrary but if we swapped both nothing would end249# up swapping at all since it would swap back.250251vars_to_swap = ['src']252for ((mod, _, width), default, opts) in st[1].get("modifiers", []):253if mod[-1] in str(src_a):254vars_to_swap.append(mod[0:-1])255256common_body.append('if {}'.format(compile_s_expr(cond, imm_map, None)) + ' {')257258# Emit the swaps. We use a temp, and wrap in a block to avoid naming259# collisions with multiple swaps. {{Doubling}} to escape the format.260261for v in vars_to_swap:262common_body.append(' {{ unsigned temp = {}{}; {}{} = {}{}; {}{} = temp; }}'.format(v, src_a, v, src_a, v, src_b, v, src_b))263264# Also, remap. Bidrectional swaps are explicit in the XML.265for v in remap:266maps = remap[v]267imm = imm_map[v]268269for i, l in enumerate(maps):270common_body.append(' {}if ({} == {}) {} = {};'.format('' if i == 0 else 'else ', v, imm[l], v, imm[maps[l]]))271272common_body.append('}')273common_body.append('')274275for (name, pos, width) in st[1].get("immediates", []):276common_body.append('unsigned {} = I->{};'.format(name, name))277common_body.append('assert({} < {});'.format(name, hex(1 << width)))278279for st in pack_exprs:280st.append('({} << {})'.format(name, pos))281282# After this, we have to branch off, since deriveds *do* vary based on state.283state_body = [[] for s in states]284285for i, (_, st) in enumerate(states):286for ((pos, width), exprs) in st.get("derived", []):287pack_derived(pos, exprs, imm_map, state_body[i], pack_exprs[i])288289# How do we pick a state? Accumulate the conditions290state_conds = [compile_s_expr(st[0], imm_map, None) for st in states] if len(states) > 1 else [None]291292if state_conds == None:293assert (states[0][0] == None)294295# Finally, we'll collect everything together296return 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)297298print(COPYRIGHT + '#include "compiler.h"')299300packs = [pack_variant(e, instructions[e]) for e in instructions]301for p in packs:302print(p)303304top_pack = Template("""unsigned305bi_pack_${'fma' if unit == '*' else 'add'}(bi_instr *I,306enum bifrost_packed_src src0,307enum bifrost_packed_src src1,308enum bifrost_packed_src src2,309enum bifrost_packed_src src3)310{311if (!I)312return bi_pack_${opname_to_c(unit + 'NOP.i32')}(I, src0, src1, src2, src3);313314% if unit == '*':315assert((1 << src0) & 0xfb);316assert((1 << src1) & 0xfb);317318% endif319switch (I->op) {320% for opcode in ops:321% if unit + opcode in instructions:322case BI_OPCODE_${opcode.replace('.', '_').upper()}:323return bi_pack_${opname_to_c(unit + opcode)}(I, src0, src1, src2, src3);324% endif325% endfor326default:327#ifndef NDEBUG328bi_print_instr(I, stderr);329#endif330unreachable("Cannot pack instruction as ${unit}");331}332}333""")334335for unit in ['*', '+']:336print(top_pack.render(ops = ir_instructions, instructions = instructions, opname_to_c = opname_to_c, unit = unit))337338339