File: isa.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 (455 lines) | stat: -rw-r--r-- 17,112 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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
"""Defining instruction set architectures."""
from __future__ import absolute_import
from collections import OrderedDict
from .predicates import And, TypePredicate
from .registers import RegClass, Register, Stack
from .ast import Apply
from .types import ValueType
from .instructions import InstructionGroup

# The typing module is only required by mypy, and we don't use these imports
# outside type comments.
try:
    from typing import Tuple, Union, Any, Iterable, Sequence, List, Set, Dict, TYPE_CHECKING  # noqa
    if TYPE_CHECKING:
        from .instructions import MaybeBoundInst, InstructionFormat  # noqa
        from .predicates import PredNode, PredKey  # noqa
        from .settings import SettingGroup  # noqa
        from .registers import RegBank  # noqa
        from .xform import XFormGroup  # noqa
        OperandConstraint = Union[RegClass, Register, int, Stack]
        ConstraintSeq = Union[OperandConstraint, Tuple[OperandConstraint, ...]]
        # Instruction specification for encodings. Allows for predicated
        # instructions.
        InstSpec = Union[MaybeBoundInst, Apply]
        BranchRange = Sequence[int]
        # A recipe predicate consisting of an ISA predicate and an instruction
        # predicate.
        RecipePred = Tuple[PredNode, PredNode]
except ImportError:
    pass


class TargetISA(object):
    """
    A target instruction set architecture.

    The `TargetISA` class collects everything known about a target ISA.

    :param name: Short mnemonic name for the ISA.
    :param instruction_groups: List of `InstructionGroup` instances that are
        relevant for this ISA.
    """

    def __init__(self, name, instruction_groups):
        # type: (str, Sequence[InstructionGroup]) -> None
        self.name = name
        self.settings = None  # type: SettingGroup
        self.instruction_groups = instruction_groups
        self.cpumodes = list()  # type: List[CPUMode]
        self.regbanks = list()  # type: List[RegBank]
        self.legalize_codes = OrderedDict()  # type: OrderedDict[XFormGroup, int]  # noqa
        # Unique copies of all predicates.
        self._predicates = dict()  # type: Dict[PredKey, PredNode]

        assert InstructionGroup._current is None,\
            "InstructionGroup {} is still open"\
            .format(InstructionGroup._current.name)

    def __str__(self):
        # type: () -> str
        return self.name

    def finish(self):
        # type: () -> TargetISA
        """
        Finish the definition of a target ISA after adding all CPU modes and
        settings.

        This computes some derived properties that are used in multiple
        places.

        :returns self:
        """
        self._collect_encoding_recipes()
        self._collect_predicates()
        self._collect_legalize_codes()
        return self

    def _collect_encoding_recipes(self):
        # type: () -> None
        """
        Collect and number all encoding recipes in use.
        """
        self.all_recipes = list()  # type: List[EncRecipe]
        rcps = set()  # type: Set[EncRecipe]
        for cpumode in self.cpumodes:
            for enc in cpumode.encodings:
                recipe = enc.recipe
                if recipe not in rcps:
                    assert recipe.number is None
                    recipe.number = len(rcps)
                    rcps.add(recipe)
                    self.all_recipes.append(recipe)
                    # Make sure ISA predicates are registered.
                    if recipe.isap:
                        recipe.isap = self.unique_pred(recipe.isap)
                        self.settings.number_predicate(recipe.isap)
                    recipe.instp = self.unique_pred(recipe.instp)

    def _collect_predicates(self):
        # type: () -> None
        """
        Collect and number all predicates in use.

        Ensures that all ISA predicates have an assigned bit number in
        `self.settings`.
        """
        self.instp_number = OrderedDict()  # type: OrderedDict[PredNode, int]
        for cpumode in self.cpumodes:
            for enc in cpumode.encodings:
                instp = enc.instp
                if instp and instp not in self.instp_number:
                    # assign predicate number starting from 0.
                    n = len(self.instp_number)
                    self.instp_number[instp] = n

                # All referenced ISA predicates must have a number in
                # `self.settings`. This may cause some parent predicates to be
                # replicated here, which is OK.
                if enc.isap:
                    self.settings.number_predicate(enc.isap)

    def _collect_legalize_codes(self):
        # type: () -> None
        """
        Make sure all legalization transforms have been assigned a code.
        """
        for cpumode in self.cpumodes:
            self.legalize_code(cpumode.default_legalize)
            for x in cpumode.type_legalize.values():
                self.legalize_code(x)

    def legalize_code(self, xgrp):
        # type: (XFormGroup) -> int
        """
        Get the legalization code for the transform group `xgrp`. Assign one if
        necessary.

        Each target ISA has its own list of legalization actions with
        associated legalize codes that appear in the encoding tables.

        This method is used to maintain the registry of legalization actions
        and their table codes.
        """
        if xgrp in self.legalize_codes:
            code = self.legalize_codes[xgrp]
        else:
            code = len(self.legalize_codes)
            self.legalize_codes[xgrp] = code
        return code

    def unique_pred(self, pred):
        # type: (PredNode) -> PredNode
        """
        Get a unique predicate that is equivalent to `pred`.
        """
        if pred is None:
            return pred
        # TODO: We could actually perform some algebraic simplifications. It's
        # not clear if it is worthwhile.
        k = pred.predicate_key()
        if k in self._predicates:
            return self._predicates[k]
        self._predicates[k] = pred
        return pred


class CPUMode(object):
    """
    A CPU mode determines which instruction encodings are active.

    All instruction encodings are associated with exactly one `CPUMode`, and
    all CPU modes are associated with exactly one `TargetISA`.

    :param name: Short mnemonic name for the CPU mode.
    :param target: Associated `TargetISA`.
    """

    def __init__(self, name, isa):
        # type: (str, TargetISA) -> None
        self.name = name
        self.isa = isa
        self.encodings = []  # type: List[Encoding]
        isa.cpumodes.append(self)

        # Tables for configuring legalization actions when no valid encoding
        # exists for an instruction.
        self.default_legalize = None  # type: XFormGroup
        self.type_legalize = OrderedDict()  # type: OrderedDict[ValueType, XFormGroup]  # noqa

    def __str__(self):
        # type: () -> str
        return self.name

    def enc(self, *args, **kwargs):
        # type: (*Any, **Any) -> None
        """
        Add a new encoding to this CPU mode.

        Arguments are the `Encoding constructor arguments, except for the first
        `CPUMode argument which is implied.
        """
        self.encodings.append(Encoding(self, *args, **kwargs))

    def legalize_type(self, default=None, **kwargs):
        # type: (XFormGroup, **XFormGroup) -> None
        """
        Configure the legalization action per controlling type variable.

        Instructions that have a controlling type variable mentioned in one of
        the arguments will be legalized according to the action specified here
        instead of  using the `legalize_default` action.

        The keyword arguments are value type names:

            mode.legalize_type(i8=widen, i16=widen, i32=expand)

        The `default` argument specifies the action to take for controlling
        type variables that don't have an explicitly configured action.
        """
        if default is not None:
            self.default_legalize = default

        for name, xgrp in kwargs.items():
            ty = ValueType.by_name(name)
            self.type_legalize[ty] = xgrp

    def legalize_monomorphic(self, xgrp):
        # type: (XFormGroup) -> None
        """
        Configure the legalization action to take for monomorphic instructions
        which don't have a controlling type variable.

        See also `legalize_type()` for polymorphic instructions.
        """
        self.type_legalize[None] = xgrp

    def get_legalize_action(self, ty):
        # type: (ValueType) -> XFormGroup
        """
        Get the legalization action to use for `ty`.
        """
        return self.type_legalize.get(ty, self.default_legalize)


class EncRecipe(object):
    """
    A recipe for encoding instructions with a given format.

    Many different instructions can be encoded by the same recipe, but they
    must all have the same instruction format.

    The `ins` and `outs` arguments are tuples specifying the register
    allocation constraints for the value operands and results respectively. The
    possible constraints for an operand are:

    - A `RegClass` specifying the set of allowed registers.
    - A `Register` specifying a fixed-register operand.
    - An integer indicating that this result is tied to a value operand, so
      they must use the same register.
    - A `Stack` specifying a value in a stack slot.

    The `branch_range` argument must be provided for recipes that can encode
    branch instructions. It is an `(origin, bits)` tuple describing the exact
    range that can be encoded in a branch instruction.

    For ISAs that use CPU flags in `iflags` and `fflags` value types, the
    `clobbers_flags` is used to indicate instruction encodings that clobbers
    the CPU flags, so they can't be used where a flag value is live.

    :param name: Short mnemonic name for this recipe.
    :param format: All encoded instructions must have this
            :py:class:`InstructionFormat`.
    :param base_size: Base number of bytes in the binary encoded instruction.
    :param compute_size: Function name to use when computing actual size.
    :param ins: Tuple of register constraints for value operands.
    :param outs: Tuple of register constraints for results.
    :param branch_range: `(origin, bits)` range for branches.
    :param clobbers_flags: This instruction clobbers `iflags` and `fflags`.
    :param instp: Instruction predicate.
    :param isap: ISA predicate.
    :param emit: Rust code for binary emission.
    """

    def __init__(
            self,
            name,                     # type: str
            format,                   # type: InstructionFormat
            base_size,                # type: int
            ins,                      # type: ConstraintSeq
            outs,                     # type: ConstraintSeq
            compute_size=None,        # type: str
            branch_range=None,        # type: BranchRange
            clobbers_flags=True,      # type: bool
            instp=None,               # type: PredNode
            isap=None,                # type: PredNode
            emit=None                 # type: str
            ):
        # type: (...) -> None
        self.name = name
        self.format = format
        assert base_size >= 0
        self.base_size = base_size
        self.compute_size = compute_size if compute_size is not None \
            else 'base_size'
        self.branch_range = branch_range
        self.clobbers_flags = clobbers_flags
        self.instp = instp
        self.isap = isap
        self.emit = emit
        if instp:
            assert instp.predicate_context() == format
        self.number = None  # type: int

        self.ins = self._verify_constraints(ins)
        if not format.has_value_list:
            assert len(self.ins) == format.num_value_operands
        self.outs = self._verify_constraints(outs)

    def __str__(self):
        # type: () -> str
        return self.name

    def _verify_constraints(self, seq):
        # type: (ConstraintSeq) -> Sequence[OperandConstraint]
        if not isinstance(seq, tuple):
            seq = (seq,)
        for c in seq:
            if isinstance(c, int):
                # An integer constraint is bound to a value operand.
                # Check that it is in range.
                assert c >= 0 and c < len(self.ins)
            else:
                assert (isinstance(c, RegClass)
                        or isinstance(c, Register)
                        or isinstance(c, Stack))
        return seq

    def ties(self):
        # type: () -> Tuple[Dict[int, int], Dict[int, int]]
        """
        Return two dictionaries representing the tied operands.

        The first maps input number to tied output number, the second maps
        output number to tied input number.
        """
        i2o = dict()  # type: Dict[int, int]
        o2i = dict()  # type: Dict[int, int]
        for o, i in enumerate(self.outs):
            if isinstance(i, int):
                i2o[i] = o
                o2i[o] = i
        return (i2o, o2i)

    def fixed_ops(self):
        # type: () -> Tuple[Set[Register], Set[Register]]
        """
        Return two sets of registers representing the fixed input and output
        operands.
        """
        i = set(r for r in self.ins if isinstance(r, Register))
        o = set(r for r in self.outs if isinstance(r, Register))
        return (i, o)

    def recipe_pred(self):
        # type: () -> RecipePred
        """
        Get the combined recipe predicate which includes both the ISA predicate
        and the instruction predicate.

        Return `None` if this recipe has neither predicate.
        """
        if self.isap is None and self.instp is None:
            return None
        else:
            return (self.isap, self.instp)


class Encoding(object):
    """
    Encoding for a concrete instruction.

    An `Encoding` object ties an instruction opcode with concrete type
    variables together with and encoding recipe and encoding bits.

    The concrete instruction can be in three different forms:

    1. A naked opcode: `trap` for non-polymorphic instructions.
    2. With bound type variables: `iadd.i32` for polymorphic instructions.
    3. With operands providing constraints: `icmp.i32(intcc.eq, x, y)`.

    If the instruction is polymorphic, all type variables must be provided.

    :param cpumode: The CPU mode where the encoding is active.
    :param inst: The :py:class:`Instruction` or :py:class:`BoundInstruction`
                 being encoded.
    :param recipe: The :py:class:`EncRecipe` to use.
    :param encbits: Additional encoding bits to be interpreted by `recipe`.
    :param instp: Instruction predicate, or `None`.
    :param isap: ISA predicate, or `None`.
    """

    def __init__(self, cpumode, inst, recipe, encbits, instp=None, isap=None):
        # type: (CPUMode, InstSpec, EncRecipe, int, PredNode, PredNode) -> None # noqa
        assert isinstance(cpumode, CPUMode)
        assert isinstance(recipe, EncRecipe)

        # Check for possible instruction predicates in `inst`.
        if isinstance(inst, Apply):
            instp = And.combine(instp, inst.inst_predicate())
            self.inst = inst.inst
            self.typevars = inst.typevars
        else:
            self.inst, self.typevars = inst.fully_bound()

            # Add secondary type variables to the instruction predicate.
            # This is already included by Apply.inst_predicate() above.
            if len(self.typevars) > 1:
                for tv, vt in zip(self.inst.other_typevars, self.typevars[1:]):
                    # A None tv is an 'any' wild card: `ishl.i32.any`.
                    if vt is None:
                        continue
                    typred = TypePredicate.typevar_check(self.inst, tv, vt)
                    instp = And.combine(instp, typred)

        self.cpumode = cpumode
        assert self.inst.format == recipe.format, (
                "Format {} must match recipe: {}".format(
                    self.inst.format, recipe.format))

        if self.inst.is_branch and not self.inst.is_indirect_branch:
            assert recipe.branch_range, (
                    'Recipe {} for {} must have a branch_range'
                    .format(recipe, self.inst.name))

        self.recipe = recipe
        self.encbits = encbits

        # Record specific predicates. Note that the recipe also has predicates.
        self.instp = self.cpumode.isa.unique_pred(instp)
        self.isap = self.cpumode.isa.unique_pred(isap)

    def __str__(self):
        # type: () -> str
        return '[{}#{:02x}]'.format(self.recipe, self.encbits)

    def ctrl_typevar(self):
        # type: () -> ValueType
        """
        Get the controlling type variable for this encoding or `None`.
        """
        if self.typevars:
            return self.typevars[0]
        else:
            return None