Exemplo n.º 1
0
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()
Exemplo n.º 2
0
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")
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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))
Exemplo n.º 6
0
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;')