""" Generate sources with instruction info. """ import srcgen import constant_hash import cretonne def gen_formats(fmt): """Generate an instruction format enumeration""" fmt.doc_comment('An instruction format') fmt.doc_comment('') fmt.doc_comment('Every opcode has a corresponding instruction format') fmt.doc_comment('which is represented by both the `InstructionFormat`') fmt.doc_comment('and the `InstructionData` enums.') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') with fmt.indented('pub enum InstructionFormat {', '}'): for f in cretonne.InstructionFormat.all_formats: fmt.line(f.name + ',') fmt.line() # Emit a From which also serves to verify that # InstructionFormat and InstructionData are in sync. with fmt.indented( "impl<'a> From<&'a InstructionData> for InstructionFormat {", '}'): with fmt.indented( "fn from(inst: &'a InstructionData) -> InstructionFormat {", '}'): with fmt.indented('match *inst {', '}'): for f in cretonne.InstructionFormat.all_formats: fmt.line(('InstructionData::{} {{ .. }} => ' + 'InstructionFormat::{},') .format(f.name, f.name)) fmt.line() def gen_instruction_data_impl(fmt): """ Generate the boring parts of the InstructionData implementation. These methods in `impl InstructionData` can be generated automatically from the instruction formats: - `pub fn opcode(&self) -> Opcode` - `pub fn first_type(&self) -> Type` - `pub fn second_result(&self) -> Option` - `pub fn second_result_mut<'a>(&'a mut self) -> Option<&'a mut Value>` """ # The `opcode` and `first_type` methods simply read the `opcode` and `ty` # members. This is really a workaround for Rust's enum types missing shared # members. with fmt.indented('impl InstructionData {', '}'): fmt.doc_comment('Get the opcode of this instruction.') with fmt.indented('pub fn opcode(&self) -> Opcode {', '}'): with fmt.indented('match *self {', '}'): for f in cretonne.InstructionFormat.all_formats: fmt.line( 'InstructionData::{} {{ opcode, .. }} => opcode,' .format(f.name)) fmt.doc_comment('Type of the first result, or `VOID`.') with fmt.indented('pub fn first_type(&self) -> Type {', '}'): with fmt.indented('match *self {', '}'): for f in cretonne.InstructionFormat.all_formats: fmt.line( 'InstructionData::{} {{ ty, .. }} => ty,' .format(f.name)) # Generate shared and mutable accessors for `second_result` which only # applies to instruction formats that can produce multiple results. # Everything else returns `None`. fmt.doc_comment('Second result value, if any.') with fmt.indented( 'pub fn second_result(&self) -> Option {', '}'): with fmt.indented('match *self {', '}'): for f in cretonne.InstructionFormat.all_formats: if not f.multiple_results: # Single or no results. fmt.line( 'InstructionData::{} {{ .. }} => None,' .format(f.name)) elif f.boxed_storage: # Multiple results, boxed storage. fmt.line( 'InstructionData::' + f.name + ' { ref data, .. }' + ' => Some(data.second_result),') else: # Multiple results, inline storage. fmt.line( 'InstructionData::' + f.name + ' { second_result, .. }' + ' => Some(second_result),') fmt.doc_comment('Mutable reference to second result value, if any.') with fmt.indented( "pub fn second_result_mut<'a>(&'a mut self) -> Option<&'a mut Value> {", '}'): with fmt.indented('match *self {', '}'): for f in cretonne.InstructionFormat.all_formats: if not f.multiple_results: # Single or no results. fmt.line( 'InstructionData::{} {{ .. }} => None,' .format(f.name)) elif f.boxed_storage: # Multiple results, boxed storage. fmt.line( 'InstructionData::' + f.name + ' { ref mut data, .. }' + ' => Some(&mut data.second_result),') else: # Multiple results, inline storage. fmt.line( 'InstructionData::' + f.name + ' { ref mut second_result, .. }' + ' => Some(second_result),') def collect_instr_groups(targets): seen = set() groups = [] for t in targets: for g in t.instruction_groups: if g not in seen: groups.append(g) seen.add(g) return groups def gen_opcodes(groups, fmt): """Generate opcode enumerations.""" fmt.doc_comment('An instruction opcode.') fmt.doc_comment('') fmt.doc_comment('All instructions from all supported targets are present.') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') instrs = [] with fmt.indented('pub enum Opcode {', '}'): fmt.line('NotAnOpcode,') for g in groups: for i in g.instructions: instrs.append(i) # Build a doc comment. prefix = ', '.join(o.name for o in i.outs) if prefix: prefix = prefix + ' = ' suffix = ', '.join(o.name for o in i.ins) fmt.doc_comment( '`{}{} {}`. ({})' .format(prefix, i.name, suffix, 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 => "",') 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() def generate(targets, out_dir): groups = collect_instr_groups(targets) # opcodes.rs fmt = srcgen.Formatter() gen_formats(fmt) gen_instruction_data_impl(fmt) gen_opcodes(groups, fmt) fmt.update_file('opcodes.rs', out_dir)