File: gen_binemit.py

package info (click to toggle)
firefox-esr 68.10.0esr-1~deb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 3,143,932 kB
  • sloc: cpp: 5,227,879; javascript: 4,315,531; ansic: 2,467,042; python: 794,975; java: 349,993; asm: 232,034; xml: 228,320; sh: 82,008; lisp: 41,202; makefile: 22,347; perl: 15,555; objc: 5,277; cs: 4,725; yacc: 1,778; ada: 1,681; pascal: 1,673; lex: 1,417; exp: 527; php: 436; ruby: 225; awk: 162; sed: 53; csh: 44
file content (170 lines) | stat: -rw-r--r-- 6,486 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"""
Generate binary emission code for each ISA.
"""

from __future__ import absolute_import
from cdsl.registers import RegClass, Stack
import srcgen

try:
    from typing import Sequence, List  # noqa
    from cdsl.isa import TargetISA, EncRecipe, OperandConstraint  # noqa
except ImportError:
    pass


def gen_recipe(recipe, fmt):
    # type: (EncRecipe, srcgen.Formatter) -> None
    """
    Generate code to handle a single recipe.

    - Unpack the instruction data, knowing the format.
    - Determine register locations for operands with register constraints.
    - Determine stack slot locations for operands with stack constraints.
    - Call hand-written code for the actual emission.
    """
    iform = recipe.format
    nvops = iform.num_value_operands
    want_args = any(isinstance(i, RegClass) or isinstance(i, Stack)
                    for i in recipe.ins)
    assert not want_args or nvops > 0 or iform.has_value_list
    want_outs = any(isinstance(o, RegClass) or isinstance(o, Stack)
                    for o in recipe.outs)

    # Regmove instructions get special treatment.
    is_regmove = (recipe.format.name in ('RegMove', 'RegSpill', 'RegFill'))

    # First unpack the instruction.
    with fmt.indented(
            'if let InstructionData::{} {{'.format(iform.name),
            '}'):
        fmt.line('opcode,')
        for f in iform.imm_fields:
            fmt.line('{},'.format(f.member))
        if want_args:
            if iform.has_value_list or nvops > 1:
                fmt.line('ref args,')
            else:
                fmt.line('arg,')
        fmt.line('..')
        fmt.outdented_line('} = func.dfg[inst] {')

        # Pass recipe arguments in this order: inputs, imm_fields, outputs.
        args = ''

        # Normalize to an `args` array.
        if want_args and not is_regmove:
            if iform.has_value_list:
                fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
            elif nvops == 1:
                fmt.line('let args = [arg];')
            args += unwrap_values(recipe.ins, 'in', 'args', fmt)

        for f in iform.imm_fields:
            args += ', ' + f.member

        # Unwrap interesting output arguments.
        if want_outs:
            if len(recipe.outs) == 1:
                fmt.line('let results = [func.dfg.first_result(inst)];')
            else:
                fmt.line('let results = func.dfg.inst_results(inst);')
            args += unwrap_values(recipe.outs, 'out', 'results', fmt)

        # Special handling for regmove instructions. Update the register
        # diversion tracker.
        if recipe.format.name == 'RegMove':
            fmt.line('divert.regmove(arg, src, dst);')
        elif recipe.format.name == 'RegSpill':
            fmt.line('divert.regspill(arg, src, dst);')
        elif recipe.format.name == 'RegFill':
            fmt.line('divert.regfill(arg, src, dst);')

        # Call hand-written code. If the recipe contains a code snippet, use
        # that. Otherwise cal a recipe function in the target ISA's binemit
        # module.
        if recipe.emit is None:
            fmt.format(
                    'return recipe_{}(func, inst, sink, bits{});',
                    recipe.name.lower(), args)
        else:
            fmt.multi_line(recipe.emit)
            fmt.line('return;')


def unwrap_values(args, prefix, values, fmt):
    # type: (Sequence[OperandConstraint], str, str, srcgen.Formatter) -> str  # noqa
    """
    Emit code that unwraps values living in registers or stack slots.

    :param args: Input or output constraints.
    :param prefix: Prefix to be used for the generated local variables.
    :param values: Name of slice containing the values to be unwrapped.
    :returns: Comma separated list of the generated variables
    """
    varlist = ''
    for i, cst in enumerate(args):
        if isinstance(cst, RegClass):
            v = '{}_reg{}'.format(prefix, i)
            varlist += ', ' + v
            fmt.format(
                    'let {} = divert.reg({}[{}], &func.locations);',
                    v, values, i)
        elif isinstance(cst, Stack):
            v = '{}_stk{}'.format(prefix, i)
            varlist += ', ' + v
            with fmt.indented(
                    'let {} = StackRef::masked('.format(v),
                    ').unwrap();'):
                fmt.format('divert.stack({}[{}], &func.locations),', values, i)
                fmt.format('{},', cst.stack_base_mask())
                fmt.line('&func.stack_slots,')
    return varlist


def gen_isa(isa, fmt):
    # type: (TargetISA, srcgen.Formatter) -> None
    """
    Generate Binary emission code for `isa`.
    """
    fmt.doc_comment(
            '''
            Emit binary machine code for `inst` for the {} ISA.
            '''.format(isa.name))
    if len(isa.all_recipes) == 0:
        # No encoding recipes: Emit a stub.
        with fmt.indented('pub fn emit_inst<CS: CodeSink + ?Sized>('):
            fmt.line('func: &Function,')
            fmt.line('inst: Inst,')
            fmt.line('_divert: &mut RegDiversions,')
            fmt.line('_sink: &mut CS,')
        with fmt.indented(') {', '}'):
            fmt.line('bad_encoding(func, inst)')
    else:
        fmt.line('#[allow(unused_variables, unreachable_code)]')
        with fmt.indented('pub fn emit_inst<CS: CodeSink + ?Sized>('):
            fmt.line('func: &Function,')
            fmt.line('inst: Inst,')
            fmt.line('divert: &mut RegDiversions,')
            fmt.line('sink: &mut CS,')
        with fmt.indented(') {', '}'):
            fmt.line('let encoding = func.encodings[inst];')
            fmt.line('let bits = encoding.bits();')
            with fmt.indented('match func.encodings[inst].recipe() {', '}'):
                for i, recipe in enumerate(isa.all_recipes):
                    fmt.comment('Recipe {}'.format(recipe.name))
                    with fmt.indented('{} => {{'.format(i), '}'):
                        gen_recipe(recipe, fmt)
                fmt.line('_ => {},')
            # Allow for un-encoded ghost instructions.
            # Verifier checks the details.
            with fmt.indented('if encoding.is_legal() {', '}'):
                fmt.line('bad_encoding(func, inst);')


def generate(isas, out_dir):
    # type: (Sequence[TargetISA], str) -> None
    for isa in isas:
        fmt = srcgen.Formatter()
        gen_isa(isa, fmt)
        fmt.update_file('binemit-{}.rs'.format(isa.name), out_dir)