def disasm_op(name, op): (mnemonic, test, desc) = op is_fma = mnemonic[0] == '*' # Modifiers may be either direct (pos is not None) or indirect (pos is # None). If direct, we just do the bit lookup. If indirect, we use a LUT. body = "" skip_mods = [] body += build_lut(mnemonic, desc, test) for ((mod, pos, width), default, opts) in desc.get('modifiers', []): if pos is not None: body += lut_template.render(field = mod, table = pretty_mods(opts, default), pos = pos, width = width) + "\n" # Mnemonic, followed by modifiers body += ' fputs("{}", fp);\n'.format(mnemonic) srcs = desc.get('srcs', []) for mod in desc.get('modifiers', []): # Skip per-source until next block if mod[0][0][-1] in "0123" and int(mod[0][0][-1]) < len(srcs): continue body += disasm_mod(mod, skip_mods) body += ' fputs(" ", fp);\n' body += ' bi_disasm_dest_{}(fp, next_regs, last);\n'.format('fma' if is_fma else 'add') # Next up, each source. Source modifiers are inserterd here for i, (pos, mask) in enumerate(srcs): body += ' fputs(", ", fp);\n' body += ' dump_src(fp, _BITS(bits, {}, 3), *srcs, branch_offset, consts, {});\n'.format(pos, "true" if is_fma else "false") # Error check if needed if (mask != 0xFF): body += ' if (!({} & (1 << _BITS(bits, {}, 3)))) fputs("(INVALID)", fp);\n'.format(hex(mask), pos, 3) # Print modifiers suffixed with this src number (e.g. abs0 for src0) for mod in desc.get('modifiers', []): if mod[0][0][-1] == str(i): body += disasm_mod(mod, skip_mods) # And each immediate for (imm, pos, width) in desc.get('immediates', []): body += ' fprintf(fp, ", {}:%u", _BITS(bits, {}, {}));\n'.format(imm, pos, width) # Attach a staging register if one is used if desc.get('staging'): body += ' fprintf(fp, ", @r%u", staging_register);\n' return disasm_op_template.render(c_name = opname_to_c(name), body = body)
def decode_op(instructions, is_fma): # Filter out the desired execution unit options = [n for n in instructions.keys() if (n[0] == '*') == is_fma] # Sort by exact masks, descending MAX_MASK = (1 << (23 if is_fma else 20)) - 1 options.sort(key=lambda n: (MAX_MASK ^ instructions[n][2]["exact"][0])) # Map to what we need to template mapped = [(opname_to_c(op), instructions[op][2]["exact"], reserved_masks(instructions[op])) for op in options] # Generate checks in order template = """void bi_disasm_${unit}(FILE *fp, unsigned bits, struct bifrost_regs *srcs, struct bifrost_regs *next_regs, unsigned staging_register, unsigned branch_offset, struct bi_constants *consts, bool last) { fputs(" ", fp); % for (i, (name, (emask, ebits), derived)) in enumerate(options): % if len(derived) > 0: ${"else " if i > 0 else ""}if (unlikely(((bits & ${hex(emask)}) == ${hex(ebits)}) % for (pos, width, reserved) in derived: && !(${hex(reserved)} & (1 << _BITS(bits, ${pos}, ${width}))) % endfor )) % else: ${"else " if i > 0 else ""}if (unlikely(((bits & ${hex(emask)}) == ${hex(ebits)}))) % endif bi_disasm_${name}(fp, bits, srcs, next_regs, staging_register, branch_offset, consts, last); % endfor else fprintf(fp, "INSTR_INVALID_ENC ${unit} %X", bits); fputs("\\n", fp); }""" return Template(template).render(options=mapped, unit="fma" if is_fma else "add")
def pack_variant(opname, states): # Expressions to be ORed together for the final pack, an array per state pack_exprs = [[hex(state[1]["exact"][1])] for state in states] # Computations which need to be done to encode first, across states common_body = [] # Map from modifier names to a map from modifier values to encoded values # String -> { String -> Uint }. This can be shared across states since # modifiers are (except the pos values) constant across state. imm_map = {} # Pack sources. Offset over to deal with staging/immediate weirdness in our # IR (TODO: reorder sources upstream so this goes away). Note sources are # constant across states. staging = states[0][1].get("staging", "") offset = 0 if staging in ["r", "rw"]: offset += 1 pack_sources(states[0][1].get("srcs", []), common_body, pack_exprs, offset) modifiers_handled = [] for st in states: for ((mod, _, width), default, opts) in st[1].get("modifiers", []): if mod in modifiers_handled: continue modifiers_handled.append(mod) if pack_modifier(mod, width, default, opts, common_body, pack_exprs) is None: return None imm_map[mod] = {x: y for y, x in enumerate(opts)} for i, st in enumerate(states): for ((mod, pos, width), default, opts) in st[1].get("modifiers", []): if pos is not None: pack_exprs[i].append('({} << {})'.format(mod, pos)) for ((src_a, src_b), cond, remap) in st[1].get("swaps", []): # Figure out which vars to swap, in order to swap the arguments. This # always includes the sources themselves, and may include source # modifiers (with the same source indices). We swap based on which # matches A, this is arbitrary but if we swapped both nothing would end # up swapping at all since it would swap back. vars_to_swap = ['src'] for ((mod, _, width), default, opts) in st[1].get("modifiers", []): if mod[-1] in str(src_a): vars_to_swap.append(mod[0:-1]) common_body.append( 'if {}'.format(compile_s_expr(cond, imm_map, None)) + ' {') # Emit the swaps. We use a temp, and wrap in a block to avoid naming # collisions with multiple swaps. {{Doubling}} to escape the format. for v in vars_to_swap: common_body.append( ' {{ unsigned temp = {}{}; {}{} = {}{}; {}{} = temp; }}'. format(v, src_a, v, src_a, v, src_b, v, src_b)) # Also, remap. Bidrectional swaps are explicit in the XML. for v in remap: maps = remap[v] imm = imm_map[v] for i, l in enumerate(maps): common_body.append(' {}if ({} == {}) {} = {};'.format( '' if i == 0 else 'else ', v, imm[l], v, imm[maps[l]])) common_body.append('}') common_body.append('') for (name, pos, width) in st[1].get("immediates", []): if name not in IMMEDIATE_TABLE: return None common_body.append('unsigned {} = {};'.format(name, IMMEDIATE_TABLE[name])) for st in pack_exprs: st.append('({} << {})'.format(name, pos)) if staging == 'r': common_body.append('bi_read_staging_register(clause, ins);') elif staging == 'w': common_body.append('bi_write_staging_register(clause, ins);') elif staging == '': pass else: assert staging == 'rw' # XXX: register allocation requirement (!) common_body.append('bi_read_staging_register(clause, ins);') common_body.append('assert(ins->src[0] == ins->dest);') # After this, we have to branch off, since deriveds *do* vary based on state. state_body = [[] for s in states] for i, (_, st) in enumerate(states): for ((pos, width), exprs) in st.get("derived", []): pack_derived(pos, exprs, imm_map, state_body[i], pack_exprs[i]) # How do we pick a state? Accumulate the conditions state_conds = [compile_s_expr(st[0], imm_map, None) for st in states] if len(states) > 1 else [None] if state_conds == None: assert (states[0][0] == None) # Finally, we'll collect everything together 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))