def _make_emit_pattern_low(self,iform): emit_pattern = [] for action in iform.rule.actions: if action.type == 'emit': # if no field_name, then we must differentiate the # emit patterns using the value to avoid collisions. if action.field_name == None: emit_pattern.append("emit {} nbits={} intval={}".format( action.field_name, action.nbits, action.int_value)) else: emit_pattern.append("emit {} nbits={}".format( action.field_name, action.nbits)) elif action.type == 'nt': emit_pattern.append(str(action)) elif action.type == 'FB': # FB are not used in emit phase so we do not factor them # in to the string that represents the pattern pass else: genutil.die("unexpected action type: %s" % action.type) emit_actions_str = ', '.join(emit_pattern) return emit_actions_str
def _verify_naked_bits_in_unique_pattern( self): # FIXME 2019-10-04 unused, no longer relevant ''' calculate how many references we have per each full instruction emit pattern. naked bits are bits in the pattern without a field name like 0x0F or 0b110. earlier functions decorated opcode/legacy map. If the naked bits just show up once, then we can hardcode those bits in the emit function. This is a test for that. Current design relies on the naked bits being the same in similar instruction patterns. If two patterns differ in any naked bits, they cannot share emit functions and we die. The workaround would be to capture the bits in some field to allow the emit function to be shared & generic. The current inputs to XED have no such conflicts. ''' refs_per_ptrn = collections.defaultdict(int) for iform in self.iform_list: refs_per_ptrn[iform.emit_actions] += 1 if refs_per_ptrn[iform.emit_actions] >= 2: if iform.rule.has_naked_bit_action(): # this assumes that the naked bits are going to be different. # if the naked bits were the same, we could share the emit action. genutil.die( 'emit pattern has more than one reference use of naked bits is not allowed: {}\n{}' .format(iform.emit_actions, iform))
def gen_find_fos(self, fname): # L2 phash obj_str = self.cdict.strings_dict['obj_str'] obj_type = self.cdict.strings_dict['obj_type'] const = self.cdict.strings_dict['obj_const'] hx2fo = {} for hx, phash in list(self.hx2phash.items()): fid = '%s_%d_l1' % (fname, hx) (hx2fo_list, operand_lu_fo) = phash.gen_find_fos(fid) if not operand_lu_fo: genutil.die("L2 hash cannot have trivial operand lu fn") hx2fo[hx] = hx2fo_list[0] fname = '%s' % fname if genutil.field_check(self.cdict, 'ntluf') or \ genutil.field_check(self.cdict, 'nt'): return_type = 'xed_uint32_t' else: return_type = self.cdict.action_codegen.get_return_type() static = self.cdict.strings_dict['static'] fo = codegen.function_object_t(fname, return_type=return_type, static=static, inline=False) fo.add_arg('%s%s* %s' % (const, obj_type, obj_str)) self.add_lu_table(fo, hx2fo) #we only need to override add_lookup_lines lu_fname = operand_lu_fo.function_name self.add_op_lu_function(fo, lu_fname) self.add_find_lines(fo) fos = list(hx2fo.values()) fos.append(fo) #all the operand_lu_fo going to be the same so we just take the last one return fos, operand_lu_fo
def _gen_tuple2int(self): ''' generate the int value of each tuple. we shift each element by the number of bits that the previous element took ''' for tuple in self.tuple2rule: res = 0 bit_shift = 0 for i,byte in enumerate(tuple): if self.cnames[i] == 'UIMM0': pass opwidth = self.op_widths[self.cnames[i]] res += byte << bit_shift bit_shift += opwidth if res not in self.int2tuple: self.tuple2int[tuple] = res self.int2tuple[res] = tuple else: conflict_tuple = self.int2tuple[res] err = "conflict in nt: %s\n" % self.nt_name err += "tuple %s = %s\n" err =err % (str(tuple),self.tuple2conditions[tuple]) err += "and tuple: %s = %s\n" err =err % (str(conflict_tuple), self.tuple2conditions[conflict_tuple]) err += "generate the same int value: %d" % res genutil.die(err)
def _unite_rules(self, nonterminal): ''' removing rules with identical constraints. if more than one rule has that same constraint then one of them must be marked as encoder preferred. bucketing the rules, each bin represents unique constraint. we go over each bin, if it has more than one rule, we look for the attribute enc_preferred ''' rules_bin = [] for rule in nonterminal.rules: found = False for bin in rules_bin: if bin[0].cdict == rule.cdict: bin.append(rule) found = True break if not found: rules_bin.append([rule]) rules = [] for bin in rules_bin: if len(bin) > 1: preferred_rule = self._encoder_preferred(bin) if preferred_rule: rules.append(preferred_rule) else: err = "in nt %s several rules has the constraint: %s\n" err += "one of them must be marked as encoder preferred\n" genutil.die(err % (nonterminal.name, bin[0].conditions)) else: rules.extend(bin) return rules
def _verify_naked_bits_in_unique_pattern(self): ''' calculate how many references we have per each full instruction emit pattern. naked bits are bits in the pattern without a field name like 0x0F or 0b110. earlier functions decorated opcode/legacy map. If the naked bits just show up once, then we can hardcode those bits in the emit function. This is a test for that. Current design relies on the naked bits being the same in similar instruction patterns. If two patterns differ in any naked bits, they cannot share emit functions and we die. The workaround would be to capture the bits in some field to allow the emit function to be shared & generic. The current inputs to XED have no such conflicts. ''' refs_per_ptrn = {} for iform in self.iform_list: if iform.emit_actions not in refs_per_ptrn: refs_per_ptrn[iform.emit_actions] = 1 else: refs_per_ptrn[iform.emit_actions] += 1 if iform.rule.has_naked_bit_action(): err = 'emit pattern: %s has more than one reference ' +\ 'use of naked bits is not allowed'%iform.emit_actions genutil.die(err)
def _generate_code_for_emit_action(self,bind_or_emit): """Emit code for emit action """ if bind_or_emit == 'BIND': if self.emit_type == 'letters' or self.field_name == None: return '' elif self.emit_type == 'numeric': op_accessor = encutil.enc_strings['op_accessor'] operand_setter = "%s_set_%s" % (op_accessor, self.field_name.lower()) obj_name = encutil.enc_strings['obj_str'] hex_val = hex(self.int_value) code = "%s(%s, %s);" % (operand_setter, obj_name, hex_val) return [' ' + code] else: genutil.die("Unknown emit_type %s" % self.emit_type) else: # EMIT emit_util_function = encutil.enc_strings['emit_util_function'] obj_name = encutil.enc_strings['obj_str'] nbits = self.nbits code = '' if self.field_name == None: if self.emit_type == 'numeric': hex_val = hex(self.int_value) code = "%s(%s, %d, %s);" % (emit_util_function,obj_name, nbits,hex_val) else: genutil.die("must have field name for letter action") else: op_accessor = encutil.enc_strings['op_accessor'] operand_getter = "%s_get_%s(%s)" % (op_accessor, self.field_name.lower(), obj_name) code = "%s(%s, %d, %s);" % (emit_util_function, obj_name,nbits,operand_getter) return [' ' + code]
def work(self): # main entry point ''' Each instruction has 1) conditions (iclass, user registers, user inputs) and 2) actions. 3 types: 2a) field bindings, 2b) nonterminals, 2c) bit-emit of operand fields (hard-coded or from NT output)). fos = function output object (plural) generate the following: 1) list of emit patterns fos (2c) 2) list of field bindings patterns fos (2a) 3) list of all field bindings values (values from prev step) 4) max number of emit patterns 5) max number of field binding patterns 6) max number of field bindings values 7) list of groups fos (see explanation in instructions_group_t) ''' for iform in self.iform_list: self._identify_map_and_nominal_opcode(iform) self._make_field_bindings_pattern(iform) self._make_emit_pattern(iform) #see explanation about bind patterns in instructions_group_t self._make_bind_pattern(iform) #self._study_emit_patterns() #self._verify_naked_bits_in_unique_pattern() self.fb_values_list = self._make_fb_values_list() # step 3 self.fb_values_table_size = len(self.fb_values_list) self.emit_ptrs_fo_list = self._make_emit_pattern_fos() self.max_emit_ptrns = len(self.emit_ptrs_fo_list) if self.max_emit_ptrns > max_in_byte: # we are using uint8 to hold the number of patterns, # we need to make sure we don't exceeds error = "total number of emit patterns(%d) exceeds 8 bits" genutil.die(error % self.max_emit_ptrns) self.instruction_groups = instructions_group_t(self.iarray, self.logs_dir) self.fb_ptrs_fo_list = self._make_fb_pattern_fos() self.max_fb_ptrns = len(self.fb_ptrs_fo_list) if self.max_fb_ptrns > max_in_byte: # we are using uint8to hold the number of patterns, # we need to make sure we don't exceeds error = "total number of field binding patterns(%d) exceeds 8 bits" genutil.die(error % self.max_fb_ptrns) if verbosity.vencode(): self._print_log()
def classify(self): if patterns.decimal_pattern.match(self.value): self.emit_type = 'numeric' self.int_value = int(self.value) t = hex(self.int_value) self.nbits = 4 * len(t[2:]) if vclassify(): msgb("CLASSIFY", "%s as decimal values" % (self.value)) return if patterns.hex_pattern.match(self.value): self.emit_type = 'numeric' self.int_value = int(self.value, 16) self.nbits = 4 * (len(self.value) - 2 ) # drop the 0x, convert nibbles to bits if vclassify(): msgb("CLASSIFY", "%s as hex" % (self.value)) return if patterns.letter_and_underscore_pattern.match(self.value): self.emit_type = 'letters' t = self.value t = genutil.no_underscores(t) self.nbits = len(t) if vclassify(): msgb("CLASSIFY", "%s as letters" % (self.value)) return b = patterns.binary_pattern.match(self.value) # leading "0b" if b: self.emit_type = 'numeric' t = '0b' + b.group('bits') # pattern match strips out 0b self.int_value = genutil.make_numeric(t) bits_str = genutil.make_binary(t) self.nbits = len(bits_str) if vclassify(): msgb( "CLASSIFY", "%s as explicit-binary -> int = %d nbits=%d [%s,%s]" % (self.value, self.int_value, self.nbits, t, bits_str)) return if patterns.bits_and_letters_underscore_pattern.match(self.value): self.emit_type = 'letters' v = genutil.no_underscores(self.value) self.nbits = len(v) if vclassify(): msgb("CLASSIFY", "%s as mixed-letters" % (self.value)) return if patterns.simple_number_pattern.match(self.value): self.emit_type = 'numeric' self.int_value = genutil.make_numeric(self.value) t = hex(self.int_value) self.nbits = 4 * len(t[2:]) if vclassify(): msgb("CLASSIFY", "%s as simple-number" % (self.value)) return genutil.die("unknown pattern")
def get_getter_fn(ptrn_list): if len(ptrn_list) == 0: genutil.die("P2341: SHOULD NOT REACH HERE") first = ptrn_list[0] for cur in ptrn_list[1:]: if first.easz_nt_seq != cur.easz_nt_seq: #conflict in easz resolution functions.. should not happen return None return ild_codegen.get_derived_op_getter_fn(first.easz_nt_seq, _easz_token)
def _check_duplications(self, regs): ''' n^2 loop which verifies that each reg exists only once. ''' for reg in regs: count = 0 for r in regs: if reg == r: count += 1 if count > 1: genutil.die("reg %s defined more than once" % reg)
def get_str_value(self): if self.is_field_binding() or self.is_return(): return self.value if self.is_nonterminal(): return self.nt if self.is_ntluf(): return self.ntluf err = "unsupported type: %s for function get_str_value" % self.type genutil.die(err)
def _make_tuple2rule(self): ''' generate the tuple that represents the constraint e.g.: for the constraint: MODE=0 EASZ=1 the tuple is (0,1) if a rule does not have constraint over certain operand then we splatter all the possible values ''' verbose=False if verbose: print("_make_tuple2rule") for rule in self.rules: #print "\t RULE", str(rule) # ctup is a list of tuples of all possible value # combinations for the constraints. ctup = [] first = True for cname in self.cnames: if verbose: print("CNAME: {}".format(cname)) new_ctup = [] if cname in rule.cdict: vals = rule.cdict[cname] if verbose: print("\tTHIS RULE VALS: {}".format(vals)) else: vals = self.state_space[cname] if verbose: print("\tSTATE SPACE VALS: {}".format(vals)) if first: first = False for val in vals: ctup.append((val,)) if verbose: print("\tFIRST CTUP: {}".format(ctup)) continue else: # cross product of constraints for val in vals: for c in ctup: new_ctup.append(c+(val,)) if verbose: print("\tNEW_CTUP: {}".format(new_ctup)) ctup = new_ctup for tupl in ctup: if tupl not in self.tuple2rule: if verbose: print("TUPLE: {} RULE: {}".format(tupl, rule)) self.tuple2rule[tupl] = rule self.tuple2conditions[tupl] = rule.conditions else: err = "in nt {}\n".format(self.nt_name) err += "generated tuple for constraint {} already exists\n" err = err.format(str(rule.cdict)) if verbose: print(err) genutil.die(err)
def __init__( self, name, aggtype, ctype, bitwidth, default_visibility=None, default_initializer=None, xprint='NOPRINT', internal_or_public="INTERNAL", dio="DO", eio="EO", ): self.name = name self.aggtype = aggtype self.ctype = ctype self.bitwidth = int(bitwidth) self.default_visibility = default_visibility self.xprint = xprint self.internal_or_public = internal_or_public self.dio = dio self.eio = eio if self.eio in ['EI', 'EO']: pass else: err = "Bad Encoder IO value: %s -- need one of {EI,EO}" genutil.die(err % self.eio) if self.dio in ['DI', 'DO', 'DS']: pass else: err = "Bad decoder IO value: %s -- need one of {DI,DO,DS}" genutil.die(err % self.eio) if self.eio == 'EI': self.encoder_input = True else: self.encoder_input = False if self.dio == 'DS': self.decoder_skip = True else: self.decoder_skip = False #NOTE: this next field is only used if initialize_each_field is True. self.default_initializer = default_initializer self.is_enum = 'enum' in self.ctype # this is the C type that will be used in the operand storage struct. self.storage_type = None #if True using bit fields self.compressed = False
def _cond_is_UIMM0_1(self, cond): ''' check whether the condition is UIMM0=1. die if the operand is UIMM0 but the value is different than 1''' if cond.field_name == 'UIMM0': if cond.rvalue.value == '1': return True else: #the operand is UIMM0 but the value is not 1 err = ('not expecting UIMM0 will have any other constraint ' + 'other than UIMM0=1') genutil.die(err) return False
def _make_emit_fo(self, iform, i): ''' create the function object for this emit pattern @param iform: iform_t object @param i: index of the pattern function @return: function_object_t ''' fname = "%s_%d" % (emit_function_prefix, i) fo = codegen.function_object_t(fname, return_type='void') # obj_str is the function parameters for the emit function obj_str = encutil.enc_strings['obj_str'] enc_arg = "%s* %s" % (encutil.enc_strings['obj_type'], obj_str) fo.add_arg(enc_arg) for action in iform.rule.actions: # MASSIVE HACK: we store the legacy_map as MAP0 in # xed_encode_iform_db[] (obj/xed-encoder-iforms-init.c) # for VEX/EVEX/XOP instr (see # _identify_map_and_nominal_opcode() ) to avoid emitting # any escape/map bytes at runtime. if action.field_name == 'MAP': if iform.encspace == 0: # legacy genutil.die("Should not see MAP here: {}".format( iform.iclass)) pass elif action.field_name and action.field_name in [ 'LEGACY_MAP1', 'LEGACY_MAP2', 'LEGACY_MAP3', 'LEGACY_MAP3DNOW' ]: if iform.encspace != 0: # legacy genutil.die("This should only occur for legacy instr") self._emit_legacy_map(fo, iform) elif action.field_name and action.field_name == 'NOM_OPCODE': code = '' get_opcode = 'xed_encoder_get_nominal_opcode(%s)' % obj_str if action.nbits == 8: emit_func = 'xed_encoder_request_emit_bytes' else: emit_func = 'xed_encoder_request_encode_emit' code = ' ' * 4 code += '%s(%s,%d,%s)' % (emit_func, obj_str, action.nbits, get_opcode) fo.add_code_eol(code) else: code = action.emit_code('EMIT') for c in code: fo.add_code(c) return fo
def read_file(fn): lines = open(fn, 'r').readlines() lines = map(genutil.no_comments, lines) lines = list(filter(genutil.blank_line, lines)) d = {} # isa-set to list of cpuid records for line in lines: wrds = line.split(':') isa_set = wrds[0].strip() cpuid_bits = wrds[1].upper().split() if isa_set in d: msg = "Duplicate cpuid definition for isa set. isa-set={} old={} new={}" genutil.die(msg.format(isa_set, d[isa_set], cpuid_bits)) d[isa_set] = cpuid_bits return d
def build_init(operand_names, dct, nelem): """Given a list of operand names and a dictory build by build_operand_bitvector, return an data initialization string""" values = [0] * nelem for o in operand_names: try: (xed_operand, biti, chunki, pos_in_chunk, mask) = dct[o] except: genutil.die("Could not find %s in operand names" % (o)) values[chunki] |= mask t = [] for v in values: t.append("%s" % (hex(v))) s = "XED_BIT_PAIR( %s )" % ",".join(t) return s
def _capture_cnames(self): ''' capture the superset of all constraint names ''' if not self.rules: return [] cnames = set() for rule in self.rules: cnames.update(set(rule.cdict.keys())) cnames = list(cnames) cnames.sort() if not cnames: msg = "XED found rules without any constraints in nt %s" genutil.die(msg % self.nt_name) return cnames
def expand_all_slashes(s): global slash2_macro_pattern a = s m = slash2_macro_pattern.search(a) while m: n = int(m.group('number')) if n > 99: genutil.die( "Hit a very large number %d when explanding slash patterns in [%s]" % (n, s)) new = n * m.group('letter') old = '%s/%s' % (m.group('letter'), m.group('number')) #print "old %s -> new %s" % (old,new) a = a.replace(old, new, 1) m = slash2_macro_pattern.search(a) return a
def prepare_lines(self): """Convert the lines to the appropriate type for emitting the enumeration""" self.tuples = [] for line in self.lines: if isinstance(line, enumer.enumer_value_t): self.tuples.append(line) elif type(line) == tuple: if len(line) == 3: (token, value, comment) = line else: genutil.die("Cannot handle line: %s" % (str(line))) token = self.prep_name(token) self.tuples.append(enumer.enumer_value_t(token, value, comment)) else: token = self.prep_name(line) self.tuples.append(enumer.enumer_value_t(token))
def _compute_type_in_storage(self): ''' detect the minimal C type data type can be used to represent the operand. the accessors will cast the operand to its C type according to the data files''' for op in list(self.operand_fields.values()): width = op.bitwidth if width <= 8: op.storage_type = 'xed_uint8_t' elif width <= 16: op.storage_type = 'xed_uint16_t' elif width <= 32: op.storage_type = 'xed_uint32_t' elif width <= 64: op.storage_type = 'xed_uint64_t' else: genutil.die("unhandled width")
def read(self, lines): """Read lines from lines until a new header or a blank line is reached""" started = False while 1: if not lines: break self.nlines += 1 line = lines[0] line = line.strip() line = re.sub(r'#.*','',line) m= constant_table_t.match_blank.match(line) if m: del lines[0] continue m= constant_table_t.match_header.match(line) if m: if started: return else: started = True del lines[0] self.name = m.group('name') self.operand = m.group('operand') continue m = constant_table_t.match_pair.match(line) if m: value = m.group('value') symbol = m.group('symbol') numeric_value = genutil.make_numeric(value) #print "INPUT: [%s] [%s]" % (value,symbol) self.value_string_pairs.append((numeric_value,symbol)) del lines[0] continue m = constant_table_t.match_pair_error.match(line) if m: value = m.group('value') numeric_value = genutil.make_numeric(value) self.value_string_pairs.append((numeric_value,None)) del lines[0] continue else: genutil.die("Could not parse line %d: [%s]\n\n" % (self.nlines,line))
def _has_VEXDEST210_equals_7_restriction(cnames, ptrn_list): """Return true if some pattern in the list of input pattern_t's has a VVVV=1111 restriction. If that occurs, we can replace all VEXDEST210 restricions since there are no other kinds of VVVV restrictions. """ if _vd_token not in cnames: return False for ptrn in ptrn_list: if _vd_token_7 in ptrn.constraints: genutil.die("XXX VEXDEST210_7 already exists in the patterns.") for ptrn in ptrn_list: if _vd_token in ptrn.constraints: cvals = ptrn.constraints[_vd_token] if len(cvals) == 1 and 7 in cvals: return True return False
def create_tuple2int(self, all_ops_widths): '''create the mapping of tuple to its int value by CONCATENTATING all the input constraint values to make an integer that is ultimately the input to the hash function. ''' tuple2int = {} int2tuple = {} for t in self.tuple2rule.keys(): res = tup2int.tuple2int(t, self.cnames, all_ops_widths) if res in int2tuple: err = "the tuple % and the tuple %s generate the same value:%d" genutil.die(err % (t,str(int2tuple[res]),res)) else: tuple2int[t] = res int2tuple[res] = t #later using the op_widths for the code generation self.op_widths = all_ops_widths self.tuple2int = tuple2int self.int2tuple = int2tuple
def _fix_bit_width_for_enums(self, agi): ''' the default width of the nums is to big and wasteful. we get the list of all values for each enum in agi and set the bitwidth to the minimal width needed. ''' # mx_bits is a mapping from enum name to the minimal number # of bits required to represent it max_bits_for_enum = self._gen_max_bits_per_enum(agi.all_enums) for op in list(self.operand_fields.values()): if op.ctype in max_bits_for_enum: needed_bits = max_bits_for_enum[op.ctype] if op.bitwidth < needed_bits: # verify that the specified bitwidth form the data files # is not smaller than the calculated vals = agi.all_enums[op.ctype] err = 'bit width for % is to small, has %d values' genutil.die(err % (op.name, vals)) else: op.bitwidth = max_bits_for_enum[op.ctype]
def _make_field_bindings_pattern(self, iform): ''' create the string that represents the field bindings pattern. ''' bind_actions = [] for action in iform.rule.actions: if action.type == 'nt': pass elif action.type == 'FB': bind_actions.append(action.field_name) elif action.type == 'emit': if action.emit_type == 'numeric' and action.field_name: bind_actions.append(action.field_name) else: pass else: genutil.die("unexpected action type: %s" % action.type) fb_ptrn = '' if bind_actions: fb_ptrn = ', '.join(sorted(bind_actions)) iform.fb_ptrn = fb_ptrn
def _make_emit_pattern(self, iform): ''' create the string that represents the action for the emit phase. using this string we will classify all the emit actions into patterns ''' emit_pattern = [] for action in iform.rule.actions: if action.type == 'emit': emit_pattern.append("emit %s nbits=%d" % (action.field_name, action.nbits)) elif action.type == 'nt': emit_pattern.append(str(action)) elif action.type == 'FB': # FB are not used in emit phase so we do not factor them # in to the string that represents the pattern pass else: genutil.die("unexpected action type: %s" % action.type) iform.emit_actions = ', '.join(emit_pattern)
def gen_function(self): ''' returns tuple of: 1) list functions (we can have several functions in case we need to levels of hashing) 2) the operands lookup function (generates the key) ''' action_codegen = actions_codegen.actions_codegen_t(self.cvg.tuple2rule, self.cvg.default_action, self.cvg.strings_dict) self.cvg.action_codegen = action_codegen if self.cvg.no_constraints(): fo = self._gen_empty_function(self.fname) return [fo], None phash = ild_phash.gen_hash(self.cvg) if phash == None: genutil.die("Failed to find perfect hash for %s" % self.fname) if verbosity.vfuncgen(): self.cvg.log.write("%s" % phash) fos, operand_lu_fo = phash.gen_find_fos(self.fname) return fos, operand_lu_fo
def _read_constant_tables(lines, tables): """Read lines from lines until a new header or a blank line is reached""" nlines = 0 y = None for line in lines: nlines += 1 line = line.strip() line = re.sub(r'#.*', '', line) m = constant_table_t.match_blank.match(line) if m: continue m = constant_table_t.match_header.match(line) if m: # found next header name = m.group('name') y = None for t in tables: if t.name == name: y = t if not y: y = constant_table_t() tables.append(y) y.name = name y.operand = m.group('operand') continue m = constant_table_t.match_pair.match(line) if m: value = m.group('value') symbol = m.group('symbol') numeric_value = genutil.make_numeric(value) #print "INPUT: [%s] [%s]" % (value,symbol) if not y: genutil.die( "Malformed constant table line {}: [{}]\n\n".format( nlines, line)) y.value_string_pairs.append((numeric_value, symbol)) continue m = constant_table_t.match_pair_error.match(line) if m: value = m.group('value') numeric_value = genutil.make_numeric(value) if not y: genutil.die( "Malformed constant table line {}: [{}]\n\n".format( nlines, line)) y.value_string_pairs.append((numeric_value, None)) continue else: genutil.die("Could not parse line {}: [{}]\n\n".format( nlines, line))