def gen_formats(fmt): # type: (srcgen.Formatter) -> None """Generate an instruction format enumeration""" fmt.doc_comment(''' An instruction format Every opcode has a corresponding instruction format which is represented by both the `InstructionFormat` and the `InstructionData` enums. ''') fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') with fmt.indented('pub enum InstructionFormat {', '}'): for f in InstructionFormat.all_formats: fmt.doc_comment(str(f)) fmt.line(f.name + ',') fmt.line() # Emit a From<InstructionData> 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) -> Self {", '}'): m = srcgen.Match('*inst') for f in InstructionFormat.all_formats: m.arm('InstructionData::' + f.name, ['..'], 'InstructionFormat::' + f.name) fmt.match(m) fmt.line()
def gen_getter(setting, sgrp, fmt): # type: (Setting, SettingGroup, srcgen.Formatter) -> None """ Emit a getter function for `setting`. """ fmt.doc_comment(setting.__doc__) if isinstance(setting, BoolSetting): proto = 'pub fn {}(&self) -> bool'.format(setting.name) with fmt.indented(proto + ' {', '}'): fmt.line( 'self.numbered_predicate({})' .format(sgrp.predicate_number[setting])) elif isinstance(setting, NumSetting): proto = 'pub fn {}(&self) -> u8'.format(setting.name) with fmt.indented(proto + ' {', '}'): fmt.line('self.bytes[{}]'.format(setting.byte_offset)) elif isinstance(setting, EnumSetting): ty = camel_case(setting.name) proto = 'pub fn {}(&self) -> {}'.format(setting.name, ty) with fmt.indented(proto + ' {', '}'): m = srcgen.Match('self.bytes[{}]'.format(setting.byte_offset)) for i, v in enumerate(setting.values): m.arm(str(i), [], '{}::{}'.format(ty, camel_case(v))) m.arm('_', [], 'panic!("Invalid enum value")') fmt.match(m) else: raise AssertionError("Unknown setting kind")
def gen_arguments_method(fmt, is_mut): # type: (srcgen.Formatter, bool) -> None method = 'arguments' mut = '' rslice = 'ref_slice' as_slice = 'as_slice' if is_mut: method += '_mut' mut = 'mut ' rslice += '_mut' as_slice = 'as_mut_slice' with fmt.indented( 'pub fn {f}<\'a>(&\'a {m}self, ' 'pool: &\'a {m}ir::ValueListPool) -> ' '&{m}[Value] {{' .format(f=method, m=mut), '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name # Formats with a value list put all of their arguments in the # list. We don't split them up, just return it all as variable # arguments. (I expect the distinction to go away). if f.has_value_list: m.arm(n, ['ref {}args'.format(mut), '..'], 'args.{}(pool)'.format(as_slice)) continue # Fixed args. fields = [] if f.num_value_operands == 0: arg = '&{}[]'.format(mut) elif f.num_value_operands == 1: fields.append('ref {}arg'.format(mut)) arg = '{}(arg)'.format(rslice) else: args = 'args_arity{}'.format(f.num_value_operands) fields.append('args: ref {}{}'.format(mut, args)) arg = args fields.append('..') m.arm(n, fields, arg) fmt.match(m)
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_instruction_data_impl(fmt): # type: (srcgen.Formatter) -> None """ 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 arguments(&self, &pool) -> &[Value]` - `pub fn arguments_mut(&mut self, &pool) -> &mut [Value]` - `pub fn take_value_list(&mut self) -> Option<ir::ValueList>` - `pub fn put_value_list(&mut self, args: ir::ValueList>` - `pub fn eq(&self, &other: Self, &pool) -> bool` - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)` """ # The `opcode` method simply reads the `opcode` 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 {', '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: m.arm('InstructionData::' + f.name, ['opcode', '..'], 'opcode') fmt.match(m) fmt.line() fmt.doc_comment('Get the controlling type variable operand.') with fmt.indented( 'pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> ' 'Option<Value> {', '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name if f.typevar_operand is None: m.arm(n, ['..'], 'None') elif f.has_value_list: # We keep all arguments in a value list. i = f.typevar_operand m.arm(n, ['ref args', '..'], 'args.get({}, pool)'.format(i)) elif f.num_value_operands == 1: # We have a single value operand called 'arg'. m.arm(n, ['arg', '..'], 'Some(arg)') else: # We have multiple value operands and an array `args`. # Which `args` index to use? args = 'args_arity{}'.format(f.num_value_operands) m.arm(n, ['args: ref {}'.format(args), '..'], 'Some({}[{}])'.format(args, f.typevar_operand)) fmt.match(m) fmt.line() fmt.doc_comment(""" Get the value arguments to this instruction. """) gen_arguments_method(fmt, False) fmt.line() fmt.doc_comment(""" Get mutable references to the value arguments to this instruction. """) gen_arguments_method(fmt, True) fmt.line() fmt.doc_comment(""" Take out the value list with all the value arguments and return it. This leaves the value list in the instruction empty. Use `put_value_list` to put the value list back. """) with fmt.indented( 'pub fn take_value_list(&mut self) -> Option<ir::ValueList> {', '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name if f.has_value_list: m.arm(n, ['ref mut args', '..'], 'Some(args.take())') m.arm('_', [], 'None') fmt.match(m) fmt.line() fmt.doc_comment(""" Put back a value list. After removing a value list with `take_value_list()`, use this method to put it back. It is required that this instruction has a format that accepts a value list, and that the existing value list is empty. This avoids leaking list pool memory. """) with fmt.indented( 'pub fn put_value_list(&mut self, vlist: ir::ValueList) {', '}'): with fmt.indented('let args = match *self {', '};'): for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name if f.has_value_list: fmt.line(n + ' { ref mut args, .. } => args,') fmt.line('_ => panic!("No value list: {:?}", self),') fmt.line( 'debug_assert!(args.is_empty(), "Value list already in use");') fmt.line('*args = vlist;') fmt.line() fmt.doc_comment(""" Compare two `InstructionData` for equality. This operation requires a reference to a `ValueListPool` to determine if the contents of any `ValueLists` are equal. """) with fmt.indented( 'pub fn eq(&self, other: &Self, pool: &ir::ValueListPool)' ' -> bool {', '}'): with fmt.indented( 'if ::std::mem::discriminant(self) != ' '::std::mem::discriminant(other) {', '}'): fmt.line('return false;') with fmt.indented('match (self, other) {', '}'): for f in InstructionFormat.all_formats: n = '&InstructionData::' + f.name members = ['opcode'] if f.typevar_operand is None: args_eq = None elif f.has_value_list: members.append('args') args_eq = 'args1.as_slice(pool) == ' \ 'args2.as_slice(pool)' elif f.num_value_operands == 1: members.append('arg') args_eq = 'arg1 == arg2' else: members.append('args') args_eq = 'args1 == args2' for field in f.imm_fields: members.append(field.member) pat1 = ', '.join('{}: ref {}1'.format(x, x) for x in members) pat2 = ', '.join('{}: ref {}2'.format(x, x) for x in members) with fmt.indented( '({} {{ {} }}, {} {{ {} }}) => {{'.format( n, pat1, n, pat2), '}'): fmt.line('opcode1 == opcode2') for field in f.imm_fields: fmt.line('&& {}1 == {}2'.format( field.member, field.member)) if args_eq is not None: fmt.line('&& {}'.format(args_eq)) fmt.line('_ => unreachable!()') fmt.line() fmt.doc_comment(""" Hash an `InstructionData`. This operation requires a reference to a `ValueListPool` to hash the contents of any `ValueLists`. """) with fmt.indented( 'pub fn hash<H: ::std::hash::Hasher>' '(&self, state: &mut H, pool: &ir::ValueListPool) {', '}'): with fmt.indented('match *self {', '}'): for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name members = ['opcode'] if f.typevar_operand is None: args = '&()' elif f.has_value_list: members.append('ref args') args = 'args.as_slice(pool)' elif f.num_value_operands == 1: members.append('ref arg') args = 'arg' else: members.append('ref args') args = 'args' for field in f.imm_fields: members.append(field.member) pat = n + ' { ' + ', '.join(members) + ' }' with fmt.indented(pat + ' => {', '}'): fmt.line('::std::hash::Hash::hash( ' '&::std::mem::discriminant(self), state);') fmt.line('::std::hash::Hash::hash(&opcode, state);') for field in f.imm_fields: fmt.line( '::std::hash::Hash::hash(&{}, state);'.format( field.member)) fmt.line( '::std::hash::Hash::hash({}, state);'.format(args))
def gen_instruction_data_impl(fmt): # type: (srcgen.Formatter) -> None """ 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 arguments(&self, &pool) -> &[Value]` - `pub fn arguments_mut(&mut self, &pool) -> &mut [Value]` - `pub fn take_value_list(&mut self) -> Option<ir::ValueList>` - `pub fn put_value_list(&mut self, args: ir::ValueList>` """ # The `opcode` method simply reads the `opcode` 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 {', '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: m.arm('InstructionData::' + f.name, ['opcode', '..'], 'opcode') fmt.match(m) fmt.line() fmt.doc_comment('Get the controlling type variable operand.') with fmt.indented( 'pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> ' 'Option<Value> {', '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name if f.typevar_operand is None: m.arm(n, ['..'], 'None') elif f.has_value_list: # We keep all arguments in a value list. i = f.typevar_operand m.arm(n, ['ref args', '..'], 'args.get({}, pool)'.format(i)) elif f.num_value_operands == 1: # We have a single value operand called 'arg'. m.arm(n, ['arg', '..'], 'Some(arg)') else: # We have multiple value operands and an array `args`. # Which `args` index to use? args = 'args_arity{}'.format(f.num_value_operands) m.arm(n, ['args: ref {}'.format(args), '..'], 'Some({}[{}])'.format(args, f.typevar_operand)) fmt.match(m) fmt.line() fmt.doc_comment(""" Get the value arguments to this instruction. """) gen_arguments_method(fmt, False) fmt.line() fmt.doc_comment(""" Get mutable references to the value arguments to this instruction. """) gen_arguments_method(fmt, True) fmt.line() fmt.doc_comment(""" Take out the value list with all the value arguments and return it. This leaves the value list in the instruction empty. Use `put_value_list` to put the value list back. """) with fmt.indented( 'pub fn take_value_list(&mut self) -> Option<ir::ValueList> {', '}'): m = srcgen.Match('*self') for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name if f.has_value_list: m.arm(n, ['ref mut args', '..'], 'Some(args.take())') m.arm('_', [], 'None') fmt.match(m) fmt.line() fmt.doc_comment(""" Put back a value list. After removing a value list with `take_value_list()`, use this method to put it back. It is required that this instruction has a format that accepts a value list, and that the existing value list is empty. This avoids leaking list pool memory. """) with fmt.indented( 'pub fn put_value_list(&mut self, vlist: ir::ValueList) {', '}'): with fmt.indented('let args = match *self {', '};'): for f in InstructionFormat.all_formats: n = 'InstructionData::' + f.name if f.has_value_list: fmt.line(n + ' { ref mut args, .. } => args,') fmt.line('_ => panic!("No value list: {:?}", self),') fmt.line( 'debug_assert!(args.is_empty(), "Value list already in use");') fmt.line('*args = vlist;')