def _add_switchcase_lines(fo, nt_name, gi, all_ops_widths, key_str='key', inst='d'): cdict = gi.xed3_cdict fo.add_code('switch(%s) {' % key_str) int2key = {} key2int = {} for key in list(cdict.tuple2rule.keys()): keyval = tup2int.tuple2int(key, cdict.cnames, all_ops_widths) #This checks for a nasty conflict that should never happen: #when two different tuple keys have the same integer value. #This conflict can happen when bit widths of all constraints are #bigger than 32 bit (key is uint32 currently). #In general such error will be caught by C compiler when we try #to build a key and shift more than 32 bits. #Checking here too just to be sure. #FIXME: add an assertion to constraint_dict_t constructor to check #for that? #FIXME: this doesn't really checks for integer overflow, because #python autmatically extends int32 if it overflows to int64. #Need better checking. if keyval in int2key: msg = [] msg.append('CDICT TUPLE VALUE CONFLICT in nt %s !!!!' % nt_name) msg.append('keyval %s' % keyval) msg.append('key1 %s, key2 %s' % (key, int2key[keyval])) msg.append('cdict %s') msg = '\n'.join(msg) ildutil.ild_err(msg) int2key[keyval] = key key2int[key] = keyval covered_rules = set() #we want cases sorted by value - prettier for keyval in sorted(int2key.keys()): key = int2key[keyval] rule = cdict.tuple2rule[key] if rule in covered_rules: continue covered_rules.add(rule) keys = cdict.get_all_keys_by_val(rule) for key in keys: #FIXME: move tuple2int to ild_cdict? keyval = key2int[key] fo.add_code('case %s: /*%s -> %s*/' % (keyval, key, rule)) _add_case_lines(fo, nt_name, gi, rule) fo.add_code('default:') if gi.parser_output.otherwise_ok: fo.add_code('/* otherwise_ok */') else: #FIXME: temporary using general error, later #define more specific error enum #errval = _nt_2_xed3_err_enum(nt_name) errval = 'XED_ERROR_GENERAL_ERROR' _add_op_assign_stmt(fo, _xed3_err_op, errval, inst, indent=1) fo.add_code_eol(' break') fo.add_code('}')
def gen_l1_functions_and_lookup(agi, united_lookup, disp_dict): """Compute L1(conflict resolution) functions list and disp_bytes lookup tables dict. @param agi: all generators info @param united_lookup: the 2D lookup by map-opcode to info objects list. united_lookup['0x0']['0x78'] == [ild_info1, ild_info2, ... ] @type united_lookup: {string(insn_map) : {string(opcode): [ild_info.ild_info_t]} } """ #list of L1 function objects that resolve conflicts in map-opcodes #functions. This list will be dumped to xed_ild_imm_l1.h l1_resolution_fos = [] #dictionary l1_lookup[insn_map][opcode] = l1_function_name #this dictionary will be used to dump the has_imm lookup tables l1_lookup = {} #dictionary from function body(as string) to list of function objects #with that body. #This dict will be used to bucket identical functions in order to #not define same functions more than once. l1_bucket_dict = collections.defaultdict(list) for insn_map in ild_info.get_maps(united_lookup.is_amd): l1_lookup[insn_map] = {} for opcode in range(0, 256): #look in the hardcoded resolution functions if (insn_map, hex(opcode)) in harcoded_res_functions: l1_fn = harcoded_res_functions[(insn_map, hex(opcode))] l1_lookup[insn_map][hex(opcode)] = l1_fn continue info_list = united_lookup.get_info_list(insn_map, hex(opcode)) #get only info objects with minimum priority info_list = ild_info.get_min_prio_list(info_list) is_conflict = _is_disp_conflict(info_list, disp_dict) if len(info_list) > 1 and is_conflict: l1_fo = _resolve_conflicts(agi, info_list, disp_dict) if not l1_fo: ildutil.ild_err('FAILED TO GENERATE L1 CONFLICT ' + 'RESOLUTION FUNCTION FOR DISP\n infos: %s' % "\n".join([str(info) for info in info_list])) l1_bucket_dict[l1_fo.emit_body()].append(l1_fo) l1_fn = l1_fo.function_name elif len(info_list) == 0: #if map-opcode pair is undefined the lookup function ptr is #NULL. #This will happen for opcodes like 0F in 0F map - totally #illegal opcodes, that should never be looked up in runtime. #We define NULL pointer for such map-opcodes l1_fn = '(%s)0' % (ildutil.l1_ptr_typename) else: #there are no conflicts, we can use L2 function as L1 info = info_list[0] l1_fn = get_l2_fn_from_info(info, disp_dict) l1_lookup[insn_map][hex(opcode)] = l1_fn #there are 18 L1 functions with same body (currently, may change #in future) #we are going to bucket L1 functions with identical body but different #names in order to have only one function for each unique body #FIXME: the bucketed function name is not self descriptive bucket_name = 'xed_lookup_function_DISP_BUCKET_%s_l1' cur_bucket = 0 for res_fun_list in l1_bucket_dict.values(): if len(res_fun_list) == 1: #only one such function - we should define it as is l1_resolution_fos.append(res_fun_list[0]) else: #more than one L1 function with identical body #we should define L1 function with that body #and fix references in the lookup table #the function name cur_buck_name = bucket_name % cur_bucket cur_bucket += 1 #fix references in the lookup table for res_fun in res_fun_list: for insn_map in l1_lookup.keys(): for opcode in l1_lookup[insn_map].keys(): cur_fn = l1_lookup[insn_map][opcode] if cur_fn == res_fun.function_name: l1_lookup[insn_map][opcode] = cur_buck_name #define the L1 function and add it to the list of L1 functions #to dump buck_fo = res_fun_list[0] buck_fo.function_name = cur_buck_name l1_resolution_fos.append(buck_fo) return l1_resolution_fos,l1_lookup
def work(agi, united_lookup, disp_nts, brdisp_nts, ild_gendir, eosz_dict, easz_dict, debug): """ Main entry point of the module. Generates all the L1-3 functions and dumps disp_bytes lookup tables. """ #get all used DISP NT sequences that appear in patterns #we are going to generate L1-3 functions only for these sequences all_disp_seq = get_all_disp_seq(united_lookup) #check that all sequences are actually single NTs #(each sequence has only one NT) #my observation is that they really are. This simplifies things #and we are going to rely on that. all_nts = [] for ntseq in all_disp_seq: if len(ntseq) > 1: ildutil.ild_err("Unexpected DISP NT SEQ %s" % ntseq) if len(ntseq) == 0: continue #the empty NT sequence all_nts.append(ntseq[0]) #get only those NTs that actually appear in PATTERNs disp_nts = filter(lambda(nt): nt in all_nts, disp_nts) brdisp_nts = filter(lambda(nt): nt in all_nts, brdisp_nts) debug.write('DISP SEQS: %s\n' % all_disp_seq) debug.write('DISP NTs: %s\n' % disp_nts) debug.write('BRDISP NTs: %s\n' % brdisp_nts) brdisp_dict = _gen_l3_array_dict(agi, brdisp_nts, _brdisp_token) disp_dict = _gen_l3_array_dict(agi, disp_nts, _disp_token) nt_arr_list = brdisp_dict.values() + disp_dict.values() #create function that calls all intialization functions init_f = ild_nt.gen_init_function(nt_arr_list, 'xed_ild_disp_l3_init') #dump L3 functions ild_nt.dump_lu_arrays(agi, nt_arr_list, _l3_c_fn, mbuild.join('include-private',_l3_header_fn), init_f) #create L2 functions #The thing is that we need to know for each #DISP NT whether it depends on EOSZ or EASZ and supply appropriate arg_dict #to gen_l2_func_list() l2_functions = [] eosz_op = ild_eosz.get_target_opname() easz_op = ild_easz.get_target_opname() for nt_name,array in disp_dict.items() + brdisp_dict.items(): #Some DISP NTs depend on EOSZ, others on EASZ, we need to know #that when we generate L2 functions if eosz_op in array.get_arg_names(): arg_dict = eosz_dict else: arg_dict = easz_dict flist = ild_codegen.gen_l2_func_list(agi, {nt_name:array}, arg_dict, _ild_t_disp_member) l2_functions.extend(flist) #create the doing-nothing L2 function for map-opcodes #with regular displacement resolution l2_functions.append(_gen_empty_function(agi)) #dump L2 functions l2_headers = [ild_eosz.get_ntseq_header_fn(), ild_easz.get_ntseq_header_fn(), _l3_header_fn, ildutil.ild_private_header, operand_storage.get_operand_accessors_fn()] ild_codegen.dump_flist_2_header(agi, _l2_header_fn, l2_headers, l2_functions) #create the L1 functions and lookup tables #unite brdisp and dips dictionaries disp_dict.update(brdisp_dict) #generate L1 functions and lookup tables res = gen_l1_functions_and_lookup(agi, united_lookup, disp_dict) l1_functions,l1_lookup = res #dump L1 functions ild_codegen.dump_flist_2_header(agi, _l1_header_fn, [_l2_header_fn], l1_functions) #dump lookup tables headers = [_l1_header_fn, ildutil.ild_private_header, operand_storage.get_operand_accessors_fn()] ild_codegen.dump_lookup(agi, l1_lookup, _ild_t_disp_member, _disp_lu_header_fn, headers, ildutil.l1_ptr_typename)
def gen_ph_fos(agi, cdict_by_map_opcode, is_amd, log_fn, ptrn_dict, vv): """ Returns a tuple (phash_lu_table, phash_fo_list, op_lu_list) * phash_lu_table: is a traditional 2D dict by map, opcode to a hash function name. * phash_fo_list: is a list of all phash function objects created (we might have fos that are not in lookup table - when we have 2-level hash functions). * op_lu_list: is a list for all the operands lookup functions Also writes log file for debugging. """ maps = ild_info.get_maps(is_amd) log_f = open(log_fn, 'w') cnames = set() # only for logging stats = { '0. #map-opcodes': 0, '1. #entries': 0, '2. #hentries': 0, '3. #hashes': 0, '4. #min_hashes': 0, '5. #cdict_size_1_to_10': 0, '6. #cdict_size_10_to_20': 0, '7. #cdict_size_20_to_100': 0, '8. #cdict_size_at_least_100': 0 } lu_fo_list = [] op_lu_map = {} # fn name -> fn obj phash_lu = {} # map, opcode -> fn name for insn_map in maps: phash_lu[insn_map] = {} for opcode in range(0, 256): opcode = hex(opcode) cdict = cdict_by_map_opcode[insn_map][opcode] if cdict: stats['0. #map-opcodes'] += 1 stats['1. #entries'] += len(cdict.tuple2rule) cnames = cnames.union(set(cdict.cnames)) _log(log_f, 'MAP:%s OPCODE:%s:\n%s\n' % (insn_map, opcode, cdict)) phash = ild_phash.gen_hash(cdict) if phash: _log(log_f, "%s" % phash) phash_id = 'map%s_opcode%s_vv%d' % (insn_map, opcode, vv) fname = "%s_%s" % (_find_fn_pfx, phash_id) (fo_list, op_lu_fo) = phash.gen_find_fos(fname) lu_fo_list.extend(fo_list) #hold only one instance of each function if op_lu_fo: if op_lu_fo.function_name not in op_lu_map: op_lu_map[op_lu_fo.function_name] = op_lu_fo for fo in fo_list: _log(log_f, '//find function:\n') _log(log_f, fo.emit()) _log(log_f, '-----------------------------\n') #FIXME: assumption: L2 function is last in the list #maybe return dict or tuple to make a distinction between #L2 and L1 functions? phlu_fn = lu_fo_list[-1] phash_lu[insn_map][opcode] = phlu_fn.function_name phash.update_stats(stats) else: _log(log_f, '---NOPHASH-----\n') msg = "Failed to gen phash for map %s opcode %s" ildutil.ild_err(msg % (insn_map, opcode)) else: phash_lu[insn_map][opcode] = '(xed3_find_func_t)0' _log(log_f, "cnames: %s\n" % cnames) for key in sorted(stats.keys()): _log(log_f, "%s %s\n" % (key, stats[key])) log_f.close() return phash_lu, lu_fo_list, list(op_lu_map.values())