def emit_level1_hashtable(cpumode, level1, offt, fmt): """ Emit a level 1 hash table for `cpumode`. """ hash_table = compute_quadratic( level1.tables.values(), lambda level2: level2.ty.number) with fmt.indented( 'pub static LEVEL1_{}: [Level1Entry<{}>; {}] = [' .format(cpumode.name.upper(), offt, len(hash_table)), '];'): for level2 in hash_table: if level2: l2l = int(math.log(level2.hash_table_len, 2)) assert l2l > 0, "Hash table too small" fmt.line( 'Level1Entry ' + '{{ ty: types::{}, log2len: {}, offset: {:#08x} }},' .format( level2.ty.name.upper(), l2l, level2.hash_table_offset)) else: # Empty entry. fmt.line( 'Level1Entry ' + '{ ty: types::VOID, log2len: 0, offset: 0 },')
def gen_opcodes(groups, fmt): """ Generate opcode enumerations. Return a list of all instructions. """ fmt.doc_comment('An instruction opcode.') fmt.doc_comment('') fmt.doc_comment('All instructions from all supported ISAs are present.') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') instrs = [] with fmt.indented('pub enum Opcode {', '}'): fmt.doc_comment('An invalid opcode.') fmt.line('NotAnOpcode,') for g in groups: for i in g.instructions: instrs.append(i) i.number = len(instrs) fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) # Document polymorphism. if i.is_polymorphic: if i.use_typevar_operand: fmt.doc_comment('Type inferred from {}.'.format( i.ins[i.format.typevar_operand])) # Enum variant itself. fmt.line(i.camel_name + ',') fmt.line() # Generate a private opcode_format table. with fmt.indented( 'const OPCODE_FORMAT: [InstructionFormat; {}] = ['.format( len(instrs)), '];'): for i in instrs: fmt.format('InstructionFormat::{}, // {}', i.format.name, i.name) fmt.line() # Generate a private opcode_name function. with fmt.indented('fn opcode_name(opc: Opcode) -> &\'static str {', '}'): with fmt.indented('match opc {', '}'): fmt.line('Opcode::NotAnOpcode => "<not an opcode>",') for i in instrs: fmt.format('Opcode::{} => "{}",', i.camel_name, i.name) fmt.line() # Generate an opcode hash table for looking up opcodes by name. hash_table = constant_hash.compute_quadratic( instrs, lambda i: constant_hash.simple_hash(i.name)) with fmt.indented( 'const OPCODE_HASH_TABLE: [Opcode; {}] = ['.format( len(hash_table)), '];'): for i in hash_table: if i is None: fmt.line('Opcode::NotAnOpcode,') else: fmt.format('Opcode::{},', i.camel_name) fmt.line() return instrs
def emit_level1_hashtable(cpumode, level1, offt, fmt): # type: (CPUMode, Level1Table, str, srcgen.Formatter) -> None # noqa """ Emit a level 1 hash table for `cpumode`. """ def hash_func(level2): # type: (Level2Table) -> int return level2.ty.number if level2.ty is not None else 0 hash_table = compute_quadratic(level1.tables.values(), hash_func) with fmt.indented( 'pub static LEVEL1_{}: [Level1Entry<{}>; {}] = [' .format(cpumode.name.upper(), offt, len(hash_table)), '];'): for level2 in hash_table: # Empty hash table entry. Include the default legalization action. if not level2: fmt.format( 'Level1Entry {{ ty: ir::types::VOID, log2len: !0, ' 'offset: 0, legalize: {} }},', level1.legalize_code) continue if level2.ty is not None: tyname = level2.ty.rust_name() else: tyname = 'ir::types::VOID' lcode = cpumode.isa.legalize_code(level2.legalize) # Empty level 2 table: Only a specialized legalization action, no # actual table. # Set an offset that is out of bounds, but make sure it doesn't # overflow its type when adding `1<<log2len`. if level2.is_empty(): fmt.format( 'Level1Entry {{ ' 'ty: {}, log2len: 0, offset: !0 - 1, ' 'legalize: {} }}, // {}', tyname, lcode, level2.legalize) continue # Proper level 2 hash table. l2l = int(math.log(level2.hash_table_len, 2)) assert l2l > 0, "Level2 hash table too small" fmt.format( 'Level1Entry {{ ' 'ty: {}, log2len: {}, offset: {:#08x}, ' 'legalize: {} }}, // {}', tyname, l2l, level2.hash_table_offset, lcode, level2.legalize)
def layout_hashtable(self, level2_hashtables, level2_doc): """ Compute the hash table mapping opcode -> enclist. Append the hash table to `level2_hashtables` and record the offset. """ hash_table = compute_quadratic(self.lists.values(), lambda enclist: enclist.inst.number) self.hash_table_offset = len(level2_hashtables) self.hash_table_len = len(hash_table) level2_doc[self.hash_table_offset].append( '{:06x}: {}, {} entries'.format(self.hash_table_offset, self.ty.name, self.hash_table_len)) level2_hashtables.extend(hash_table)
def gen_descriptors(sgrp, fmt): """ Generate the DESCRIPTORS and ENUMERATORS tables. """ enums = UniqueSeqTable() with fmt.indented( 'static DESCRIPTORS: [detail::Descriptor; {}] = ['.format( len(sgrp.settings)), '];'): for idx, setting in enumerate(sgrp.settings): setting.descriptor_index = idx with fmt.indented('detail::Descriptor {', '},'): fmt.line('name: "{}",'.format(setting.name)) fmt.line('offset: {},'.format(setting.byte_offset)) if isinstance(setting, BoolSetting): fmt.line( 'detail: detail::Detail::Bool {{ bit: {} }},'.format( setting.bit_offset)) elif isinstance(setting, NumSetting): fmt.line('detail: detail::Detail::Num,') elif isinstance(setting, EnumSetting): offs = enums.add(setting.values) fmt.line('detail: detail::Detail::Enum ' + '{{ last: {}, enumerators: {} }},'.format( len(setting.values) - 1, offs)) else: raise AssertionError("Unknown setting kind") with fmt.indented( 'static ENUMERATORS: [&\'static str; {}] = ['.format( len(enums.table)), '];'): for txt in enums.table: fmt.line('"{}",'.format(txt)) def hash_setting(s): return constant_hash.simple_hash(s.name) hash_table = constant_hash.compute_quadratic(sgrp.settings, hash_setting) with fmt.indented( 'static HASH_TABLE: [u16; {}] = ['.format(len(hash_table)), '];'): for h in hash_table: if h is None: fmt.line('0xffff,') else: fmt.line('{},'.format(h.descriptor_index))
def layout_hashtable(self, level2_hashtables, level2_doc): """ Compute the hash table mapping opcode -> enclist. Append the hash table to `level2_hashtables` and record the offset. """ hash_table = compute_quadratic( self.lists.values(), lambda enclist: enclist.inst.number) self.hash_table_offset = len(level2_hashtables) self.hash_table_len = len(hash_table) level2_doc[self.hash_table_offset].append( '{:06x}: {}, {} entries'.format( self.hash_table_offset, self.ty.name, self.hash_table_len)) level2_hashtables.extend(hash_table)
def layout_hashtable(self, level2_hashtables, level2_doc): # type: (List[EncList], DefaultDict[int, List[str]]) -> None """ Compute the hash table mapping opcode -> enclist. Append the hash table to `level2_hashtables` and record the offset. """ def hash_func(enclist): # type: (EncList) -> int return enclist.inst.number hash_table = compute_quadratic(self.lists.values(), hash_func) self.hash_table_offset = len(level2_hashtables) self.hash_table_len = len(hash_table) level2_doc[self.hash_table_offset].append( '{:06x}: {}, {} entries'.format(self.hash_table_offset, self.ty, self.hash_table_len)) level2_hashtables.extend(hash_table)
def gen_descriptors(sgrp, fmt): """ Generate the DESCRIPTORS and ENUMERATORS tables. """ enums = UniqueSeqTable() with fmt.indented("static DESCRIPTORS: [detail::Descriptor; {}] = [".format(len(sgrp.settings)), "];"): for idx, setting in enumerate(sgrp.settings): setting.descriptor_index = idx with fmt.indented("detail::Descriptor {", "},"): fmt.line('name: "{}",'.format(setting.name)) fmt.line("offset: {},".format(setting.byte_offset)) if isinstance(setting, BoolSetting): fmt.line("detail: detail::Detail::Bool {{ bit: {} }},".format(setting.bit_offset)) elif isinstance(setting, NumSetting): fmt.line("detail: detail::Detail::Num,") elif isinstance(setting, EnumSetting): offs = enums.add(setting.values) fmt.line( "detail: detail::Detail::Enum " + "{{ last: {}, enumerators: {} }},".format(len(setting.values) - 1, offs) ) else: raise AssertionError("Unknown setting kind") with fmt.indented("static ENUMERATORS: [&'static str; {}] = [".format(len(enums.table)), "];"): for txt in enums.table: fmt.line('"{}",'.format(txt)) def hash_setting(s): return constant_hash.simple_hash(s.name) hash_table = constant_hash.compute_quadratic(sgrp.settings, hash_setting) with fmt.indented("static HASH_TABLE: [u16; {}] = [".format(len(hash_table)), "];"): for h in hash_table: if h is None: fmt.line("0xffff,") else: fmt.line("{},".format(h.descriptor_index))
def gen_opcodes(groups, fmt): # type: (Sequence[InstructionGroup], srcgen.Formatter) -> Sequence[Instruction] # noqa """ Generate opcode enumerations. Return a list of all instructions. """ fmt.doc_comment(''' An instruction opcode. All instructions from all supported ISAs are present. ''') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]') instrs = [] # We explicitly set the discriminant of the first variant to 1, which # allows us to take advantage of the NonZero optimization, meaning that # wrapping enums can use the 0 discriminant instead of increasing the size # if the whole type, and so SIZEOF(Option<Opcode>>) == SIZEOF(Opcode) is_first_opcode = True with fmt.indented('pub enum Opcode {', '}'): for g in groups: for i in g.instructions: instrs.append(i) i.number = len(instrs) fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) # Document polymorphism. if i.is_polymorphic: if i.use_typevar_operand: opnum = i.value_opnums[i.format.typevar_operand] fmt.doc_comment('Type inferred from {}.'.format( i.ins[opnum])) # Enum variant itself. if is_first_opcode: fmt.line(i.camel_name + ' = 1,') is_first_opcode = False else: fmt.line(i.camel_name + ',') fmt.line() with fmt.indented('impl Opcode {', '}'): for attr in sorted(Instruction.ATTRIBS.keys()): fmt.doc_comment(Instruction.ATTRIBS[attr]) with fmt.indented('pub fn {}(self) -> bool {{'.format(attr), '}'): m = srcgen.Match('self') for i in instrs: if getattr(i, attr): m.arm('Opcode::' + i.camel_name, [], 'true') m.arm('_', [], 'false') fmt.match(m) fmt.line() fmt.line() # Generate a private opcode_format table. with fmt.indented( 'const OPCODE_FORMAT: [InstructionFormat; {}] = ['.format( len(instrs)), '];'): for i in instrs: fmt.format('InstructionFormat::{}, // {}', i.format.name, i.name) fmt.line() # Generate a private opcode_name function. with fmt.indented('fn opcode_name(opc: Opcode) -> &\'static str {', '}'): m = srcgen.Match('opc') for i in instrs: m.arm('Opcode::' + i.camel_name, [], '"{}"'.format(i.name)) fmt.match(m) fmt.line() # Generate an opcode hash table for looking up opcodes by name. hash_table = constant_hash.compute_quadratic( instrs, lambda i: constant_hash.simple_hash(i.name)) with fmt.indented( 'const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = ['.format( len(hash_table)), '];'): for i in hash_table: if i is None: fmt.line('None,') else: fmt.format('Some(Opcode::{}),', i.camel_name) fmt.line() return instrs
def gen_descriptors(sgrp, fmt): # type: (SettingGroup, srcgen.Formatter) -> None """ Generate the DESCRIPTORS, ENUMERATORS, and PRESETS tables. """ enums = UniqueSeqTable() with fmt.indented( 'static DESCRIPTORS: [detail::Descriptor; {}] = [' .format(len(sgrp.settings) + len(sgrp.presets)), '];'): for idx, setting in enumerate(sgrp.settings): setting.descriptor_index = idx with fmt.indented('detail::Descriptor {', '},'): fmt.line('name: "{}",'.format(setting.name)) fmt.line('offset: {},'.format(setting.byte_offset)) if isinstance(setting, BoolSetting): fmt.line( 'detail: detail::Detail::Bool {{ bit: {} }},' .format(setting.bit_offset)) elif isinstance(setting, NumSetting): fmt.line('detail: detail::Detail::Num,') elif isinstance(setting, EnumSetting): offs = enums.add(setting.values) fmt.line( 'detail: detail::Detail::Enum ' + '{{ last: {}, enumerators: {} }},' .format(len(setting.values)-1, offs)) else: raise AssertionError("Unknown setting kind") for idx, preset in enumerate(sgrp.presets): preset.descriptor_index = len(sgrp.settings) + idx with fmt.indented('detail::Descriptor {', '},'): fmt.line('name: "{}",'.format(preset.name)) fmt.line('offset: {},'.format(idx * sgrp.settings_size)) fmt.line('detail: detail::Detail::Preset,') with fmt.indented( 'static ENUMERATORS: [&str; {}] = [' .format(len(enums.table)), '];'): for txt in enums.table: fmt.line('"{}",'.format(txt)) def hash_setting(s): # type: (Union[Setting, Preset]) -> int return constant_hash.simple_hash(s.name) hash_elems = [] # type: List[Union[Setting, Preset]] hash_elems.extend(sgrp.settings) hash_elems.extend(sgrp.presets) hash_table = constant_hash.compute_quadratic(hash_elems, hash_setting) with fmt.indented( 'static HASH_TABLE: [u16; {}] = [' .format(len(hash_table)), '];'): for h in hash_table: if h is None: fmt.line('0xffff,') else: fmt.line('{},'.format(h.descriptor_index)) with fmt.indented( 'static PRESETS: [(u8, u8); {}] = [' .format(len(sgrp.presets) * sgrp.settings_size), '];'): for preset in sgrp.presets: fmt.comment(preset.name) for mask, value in preset.layout(): fmt.format('(0b{:08b}, 0b{:08b}),', mask, value)
def gen_opcodes(groups, fmt): # type: (Sequence[InstructionGroup], srcgen.Formatter) -> Sequence[Instruction] # noqa """ Generate opcode enumerations. Return a list of all instructions. """ fmt.doc_comment(''' An instruction opcode. All instructions from all supported ISAs are present. ''') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]') instrs = [] # We explicitly set the discriminant of the first variant to 1, which # allows us to take advantage of the NonZero optimization, meaning that # wrapping enums can use the 0 discriminant instead of increasing the size # if the whole type, and so SIZEOF(Option<Opcode>>) == SIZEOF(Opcode) is_first_opcode = True with fmt.indented('pub enum Opcode {', '}'): for g in groups: for i in g.instructions: instrs.append(i) i.number = len(instrs) fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) # Document polymorphism. if i.is_polymorphic: if i.use_typevar_operand: opnum = i.value_opnums[i.format.typevar_operand] fmt.doc_comment( 'Type inferred from {}.' .format(i.ins[opnum])) # Enum variant itself. if is_first_opcode: fmt.line(i.camel_name + ' = 1,') is_first_opcode = False else: fmt.line(i.camel_name + ',') fmt.line() with fmt.indented('impl Opcode {', '}'): for attr in sorted(Instruction.ATTRIBS.keys()): fmt.doc_comment(Instruction.ATTRIBS[attr]) with fmt.indented('pub fn {}(self) -> bool {{' .format(attr), '}'): with fmt.indented('match self {', '}'): for i in instrs: if getattr(i, attr): fmt.format( 'Opcode::{} => true,', i.camel_name, i.name) fmt.line('_ => false,') fmt.line() fmt.line() # Generate a private opcode_format table. with fmt.indented( 'const OPCODE_FORMAT: [InstructionFormat; {}] = [' .format(len(instrs)), '];'): for i in instrs: fmt.format( 'InstructionFormat::{}, // {}', i.format.name, i.name) fmt.line() # Generate a private opcode_name function. with fmt.indented('fn opcode_name(opc: Opcode) -> &\'static str {', '}'): with fmt.indented('match opc {', '}'): for i in instrs: fmt.format('Opcode::{} => "{}",', i.camel_name, i.name) fmt.line() # Generate an opcode hash table for looking up opcodes by name. hash_table = constant_hash.compute_quadratic( instrs, lambda i: constant_hash.simple_hash(i.name)) with fmt.indented( 'const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = [' .format(len(hash_table)), '];'): for i in hash_table: if i is None: fmt.line('None,') else: fmt.format('Some(Opcode::{}),', i.camel_name) fmt.line() return instrs
def gen_opcodes(groups, fmt): """ Generate opcode enumerations. Return a list of all instructions. """ fmt.doc_comment('An instruction opcode.') fmt.doc_comment('') fmt.doc_comment('All instructions from all supported ISAs are present.') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') instrs = [] # We explicitly set the discriminant of the first variant to 1, which # allows us to take advantage of the NonZero optimization, meaning that # wrapping enums can use the 0 discriminant instead of increasing the size # if the whole type, and so SIZEOF(Option<Opcode>>) == SIZEOF(Opcode) is_first_opcode = True with fmt.indented('pub enum Opcode {', '}'): for g in groups: for i in g.instructions: instrs.append(i) i.number = len(instrs) fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) # Document polymorphism. if i.is_polymorphic: if i.use_typevar_operand: fmt.doc_comment('Type inferred from {}.'.format( i.ins[i.format.typevar_operand])) # Enum variant itself. if is_first_opcode: fmt.line(i.camel_name + ' = 1,') is_first_opcode = False else: fmt.line(i.camel_name + ',') fmt.line() with fmt.indented('impl Opcode {', '}'): attrs = [{ 'name': 'is_branch', 'comment': 'True for all branch instructions.' }, { 'name': 'is_terminator', 'comment': 'True for instructions that terminate EBB.' }, { 'name': 'can_trap', 'comment': 'True if instruction could trap.' }] for attr in attrs: if attr != attrs[0]: fmt.line() fmt.doc_comment(attr['comment']) with fmt.indented( 'pub fn {}(self) -> bool {{'.format(attr['name']), '}'): with fmt.indented('match self {', '}'): for i in instrs: if getattr(i, attr['name']): fmt.format('Opcode::{} => true,', i.camel_name, i.name) fmt.line('_ => false') # Generate a private opcode_format table. with fmt.indented( 'const OPCODE_FORMAT: [InstructionFormat; {}] = ['.format( len(instrs)), '];'): for i in instrs: fmt.format('InstructionFormat::{}, // {}', i.format.name, i.name) fmt.line() # Generate a private opcode_name function. with fmt.indented('fn opcode_name(opc: Opcode) -> &\'static str {', '}'): with fmt.indented('match opc {', '}'): for i in instrs: fmt.format('Opcode::{} => "{}",', i.camel_name, i.name) fmt.line() # Generate an opcode hash table for looking up opcodes by name. hash_table = constant_hash.compute_quadratic( instrs, lambda i: constant_hash.simple_hash(i.name)) with fmt.indented( 'const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = ['.format( len(hash_table)), '];'): for i in hash_table: if i is None: fmt.line('None,') else: fmt.format('Some(Opcode::{}),', i.camel_name) fmt.line() return instrs
def gen_opcodes(groups, fmt): """ Generate opcode enumerations. Return a list of all instructions. """ fmt.doc_comment('An instruction opcode.') fmt.doc_comment('') fmt.doc_comment('All instructions from all supported ISAs are present.') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') instrs = [] with fmt.indented('pub enum Opcode {', '}'): fmt.doc_comment('An invalid opcode.') fmt.line('NotAnOpcode,') for g in groups: for i in g.instructions: instrs.append(i) i.number = len(instrs) fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) # Document polymorphism. if i.is_polymorphic: if i.use_typevar_operand: fmt.doc_comment( 'Type inferred from {}.' .format(i.ins[i.format.typevar_operand])) # Enum variant itself. fmt.line(i.camel_name + ',') fmt.line() # Generate a private opcode_format table. with fmt.indented( 'const OPCODE_FORMAT: [InstructionFormat; {}] = [' .format(len(instrs)), '];'): for i in instrs: fmt.format( 'InstructionFormat::{}, // {}', i.format.name, i.name) fmt.line() # Generate a private opcode_name function. with fmt.indented('fn opcode_name(opc: Opcode) -> &\'static str {', '}'): with fmt.indented('match opc {', '}'): fmt.line('Opcode::NotAnOpcode => "<not an opcode>",') for i in instrs: fmt.format('Opcode::{} => "{}",', i.camel_name, i.name) fmt.line() # Generate an opcode hash table for looking up opcodes by name. hash_table = constant_hash.compute_quadratic( instrs, lambda i: constant_hash.simple_hash(i.name)) with fmt.indented( 'const OPCODE_HASH_TABLE: [Opcode; {}] = [' .format(len(hash_table)), '];'): for i in hash_table: if i is None: fmt.line('Opcode::NotAnOpcode,') else: fmt.format('Opcode::{},', i.camel_name) fmt.line() return instrs