tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GenerateLIRFiles.py (16570B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 # This script generates jit/LIROpsGenerated.h (list of LIR instructions)
      6 # from LIROps.yaml.
      7 
      8 import io
      9 from itertools import groupby
     10 from operator import itemgetter
     11 
     12 import buildconfig
     13 import yaml
     14 from mozbuild.preprocessor import Preprocessor
     15 
     16 HEADER_TEMPLATE = """\
     17 /* This Source Code Form is subject to the terms of the Mozilla Public
     18 * License, v. 2.0. If a copy of the MPL was not distributed with this
     19 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     20 
     21 #ifndef %(includeguard)s
     22 #define %(includeguard)s
     23 
     24 /* This file is generated by jit/GenerateLIRFiles.py. Do not edit! */
     25 
     26 %(contents)s
     27 
     28 #endif // %(includeguard)s
     29 """
     30 
     31 
     32 def load_yaml(yaml_path):
     33    # First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in
     34    # the YAML file.
     35    pp = Preprocessor()
     36    pp.context.update(buildconfig.defines["ALLDEFINES"])
     37    pp.out = io.StringIO()
     38    pp.do_filter("substitution")
     39    pp.do_include(yaml_path)
     40    contents = pp.out.getvalue()
     41    return yaml.safe_load(contents)
     42 
     43 
     44 def generate_header(c_out, includeguard, contents):
     45    c_out.write(
     46        HEADER_TEMPLATE
     47        % {
     48            "includeguard": includeguard,
     49            "contents": contents,
     50        }
     51    )
     52 
     53 
     54 operand_types = {
     55    "WordSized": "LAllocation",
     56    "BoxedValue": "LBoxAllocation",
     57    "Int64": "LInt64Allocation",
     58 }
     59 
     60 
     61 result_types = {
     62    "WordSized": "1",
     63    "BoxedValue": "BOX_PIECES",
     64    "Int64": "INT64_PIECES",
     65 }
     66 
     67 
     68 # Generate the index expression for a BoxedValue operand.
     69 #
     70 # The expression has the form |num_operands + index * BOX_PIECES|, with zero
     71 # terms being omitted.
     72 def make_boxed_index(index, reg_operands):
     73    num_operands = len(reg_operands)
     74 
     75    expr = []
     76    if num_operands:
     77        expr.append(f"{num_operands}")
     78    if index:
     79        expr.append(f"{index} * BOX_PIECES")
     80    return " + ".join(expr) if expr else "0"
     81 
     82 
     83 # Generate the index expression for an Int64 operand.
     84 #
     85 # The expression has the form
     86 # |num_operands + num_value_operands * BOX_PIECES + index * INT64_PIECES|, with
     87 # zero terms being omitted.
     88 def make_int64_index(index, reg_operands, value_operands):
     89    num_operands = len(reg_operands)
     90    num_value_operands = len(value_operands)
     91 
     92    expr = []
     93    if num_operands:
     94        expr.append(f"{num_operands}")
     95    if num_value_operands:
     96        expr.append(f"{num_value_operands} * BOX_PIECES")
     97    if index:
     98        expr.append(f"{index} * INT64_PIECES")
     99    return " + ".join(expr) if expr else "0"
    100 
    101 
    102 def gen_operands(operands, defer_init):
    103    # Group operands by operand type.
    104    sorted_operands = {
    105        k: [op for op, _ in v]
    106        for k, v in groupby(sorted(operands.items(), key=itemgetter(1)), itemgetter(1))
    107    }
    108 
    109    # Exactly three operand types are supported: WordSized, BoxedValue, and Int64.
    110    if len(sorted_operands) > 3:
    111        raise Exception("Invalid operand type: " + str(sorted_operands.keys()))
    112 
    113    reg_operands = sorted_operands.get("WordSized", [])
    114    value_operands = sorted_operands.get("BoxedValue", [])
    115    int64_operands = sorted_operands.get("Int64", [])
    116 
    117    # Operand index definitions.
    118    indices = []
    119 
    120    # Parameters for the class constructor.
    121    params = []
    122 
    123    # Initializer instructions for constructor body.
    124    initializers = []
    125 
    126    # Getter definitions.
    127    getters = []
    128 
    129    # Setter definitions.
    130    setters = []
    131 
    132    # Constructor parameters are generated in the order defined in the YAML file.
    133    if not defer_init:
    134        for operand, op_type in operands.items():
    135            params.append(f"const {operand_types[op_type]}& {operand}")
    136 
    137    # First initialize all word-sized operands.
    138    for index, operand in enumerate(reg_operands):
    139        cap_operand = operand[0].upper() + operand[1:]
    140        index_value = cap_operand + "Index"
    141        init_expr = f"setOperand({index_value}, {operand});"
    142 
    143        indices.append(f"static constexpr size_t {index_value} = {index};")
    144        if not defer_init:
    145            initializers.append(init_expr)
    146        else:
    147            setters.append(
    148                f"void set{cap_operand}(const LAllocation& {operand}) {{ {init_expr} }}"
    149            )
    150        getters.append(
    151            f"const LAllocation* {operand}() const {{ return getOperand({index_value}); }}"
    152        )
    153 
    154    # Next initialize all BoxedValue operands.
    155    for box_index, operand in enumerate(value_operands):
    156        cap_operand = operand[0].upper() + operand[1:]
    157        index_value = cap_operand + "Index"
    158        init_expr = f"setBoxOperand({index_value}, {operand});"
    159 
    160        indices.append(
    161            f"static constexpr size_t {index_value} = {make_boxed_index(box_index, reg_operands)};"
    162        )
    163        if not defer_init:
    164            initializers.append(init_expr)
    165        else:
    166            setters.append(
    167                f"void {cap_operand}(const LBoxAllocation& {operand}) {{ {init_expr} }}"
    168            )
    169        getters.append(
    170            f"LBoxAllocation {operand}() const {{ return getBoxOperand({index_value}); }}"
    171        )
    172 
    173    # Finally initialize all Int64 operands.
    174    for int64_index, operand in enumerate(int64_operands):
    175        cap_operand = operand[0].upper() + operand[1:]
    176        index_value = cap_operand + "Index"
    177        init_expr = f"setInt64Operand({index_value}, {operand});"
    178 
    179        indices.append(
    180            f"static constexpr size_t {index_value} = {make_int64_index(int64_index, reg_operands, value_operands)};"
    181        )
    182        if not defer_init:
    183            initializers.append(init_expr)
    184        else:
    185            setters.append(
    186                f"void set{cap_operand}(const LInt64Allocation& {operand}) {{ {init_expr} }}"
    187            )
    188        getters.append(
    189            f"LInt64Allocation {operand}() const {{ return getInt64Operand({index_value}); }}"
    190        )
    191 
    192    # Total number of operands.
    193    num_operands = f"{len(reg_operands)}"
    194    if value_operands:
    195        num_operands += f" + {len(value_operands)} * BOX_PIECES"
    196    if int64_operands:
    197        num_operands += f" + {len(int64_operands)} * INT64_PIECES"
    198 
    199    return (
    200        num_operands,
    201        indices,
    202        params,
    203        initializers,
    204        getters,
    205        setters,
    206    )
    207 
    208 
    209 def gen_arguments(arguments):
    210    # Class member definitions.
    211    members = []
    212 
    213    # Parameters for the class constructor.
    214    params = []
    215 
    216    # Initializer instructions for the class constructor.
    217    initializers = []
    218 
    219    # Getter definitions.
    220    getters = []
    221 
    222    for arg_name in arguments:
    223        arg_type_sig = arguments[arg_name]
    224 
    225        members.append(f"{arg_type_sig} {arg_name}_;")
    226        params.append(f"{arg_type_sig} {arg_name}")
    227        initializers.append(f"{arg_name}_({arg_name})")
    228        getters.append(f"{arg_type_sig} {arg_name}() const {{ return {arg_name}_; }}")
    229 
    230    return (members, params, initializers, getters)
    231 
    232 
    233 def gen_temps(num_temps, num_temps64, defer_init):
    234    # Parameters for the class constructor.
    235    params = []
    236 
    237    # Initializer instructions for constructor body.
    238    initializers = []
    239 
    240    # Getter definitions.
    241    getters = []
    242 
    243    # Setter definitions.
    244    setters = []
    245 
    246    for temp in range(num_temps):
    247        param_decl = f"const LDefinition& temp{temp}"
    248        init_expr = f"setTemp({temp}, temp{temp});"
    249 
    250        if not defer_init:
    251            params.append(param_decl)
    252            initializers.append(init_expr)
    253        else:
    254            initializers.append(f"setTemp({temp}, LDefinition::BogusTemp());")
    255            setters.append(f"void setTemp{temp}({param_decl}) {{ {init_expr} }}")
    256        getters.append(f"const LDefinition* temp{temp}() {{ return getTemp({temp}); }}")
    257 
    258    for int64_temp in range(num_temps64):
    259        temp = num_temps + int64_temp
    260        temp_index = f"{num_temps} + {int64_temp} * INT64_PIECES"
    261        param_decl = f"const LInt64Definition& temp{temp}"
    262        init_expr = f"setInt64Temp({temp_index}, temp{temp});"
    263 
    264        if not defer_init:
    265            params.append(param_decl)
    266            initializers.append(init_expr)
    267        else:
    268            initializers.append(f"setTemp({temp}, LInt64Definition::BogusTemp());")
    269            setters.append(f"void setTemp{temp}({param_decl}) {{ {init_expr} }}")
    270        getters.append(
    271            f"LInt64Definition temp{temp}() {{ return getInt64Temp({temp_index}); }}"
    272        )
    273 
    274    # Total number of temps.
    275    num_temps_total = f"{num_temps}"
    276    if num_temps64:
    277        num_temps_total += f" + {num_temps64} * INT64_PIECES"
    278 
    279    return (num_temps_total, params, initializers, getters, setters)
    280 
    281 
    282 def gen_successors(successors):
    283    # Parameters for the class constructor.
    284    params = []
    285 
    286    # Initializer instructions for constructor body.
    287    initializers = []
    288 
    289    # Getter definitions.
    290    getters = []
    291 
    292    for index, successor in enumerate(successors or []):
    293        params.append(f"MBasicBlock* {successor}")
    294        initializers.append(f"setSuccessor({index}, {successor});")
    295        getters.append(
    296            f"MBasicBlock* {successor}() const {{ return getSuccessor({index}); }}"
    297        )
    298 
    299    return (params, initializers, getters)
    300 
    301 
    302 def gen_lir_class(
    303    name,
    304    result_type,
    305    successors,
    306    operands,
    307    arguments,
    308    num_temps,
    309    num_temps64,
    310    call_instruction,
    311    mir_op,
    312    extra_name,
    313    defer_init,
    314 ):
    315    """Generates class definition for a single LIR opcode."""
    316    class_name = "L" + name
    317 
    318    (
    319        num_operands,
    320        oper_indices,
    321        oper_params,
    322        oper_initializers,
    323        oper_getters,
    324        oper_setters,
    325    ) = gen_operands(operands, defer_init)
    326 
    327    args_members, args_params, args_initializers, args_getters = gen_arguments(
    328        arguments
    329    )
    330 
    331    num_temps_total, temp_params, temp_initializers, temp_getters, temp_setters = (
    332        gen_temps(num_temps, num_temps64, defer_init)
    333    )
    334 
    335    succ_params, succ_initializers, succ_getters = gen_successors(successors)
    336 
    337    if successors is not None:
    338        if result_type:
    339            raise Exception("Control instructions don't return a result")
    340        num_defs = len(successors)
    341        parent_class = "LControlInstructionHelper"
    342    else:
    343        num_defs = result_types[result_type] if result_type else "0"
    344        parent_class = "LInstructionHelper"
    345 
    346    constructor_init = ""
    347    if call_instruction:
    348        constructor_init += "this->setIsCall();"
    349 
    350    mir_accessor = ""
    351    if mir_op:
    352        mir_name = name if mir_op is True else mir_op
    353        mir_accessor = f"M{mir_name}* mir() const {{ return mir_->to{mir_name}(); }};"
    354 
    355    extra_name_decl = ""
    356    if extra_name:
    357        extra_name_decl = "inline const char* extraName() const;"
    358 
    359    # Can be moved into the f-string when we use Python 3.12, see PEP 701.
    360    def nl(ws):
    361        return "\n" + ws
    362 
    363    code = f"""
    364 class {class_name} : public {parent_class}<{num_defs}, {num_operands}, {num_temps_total}> {{
    365  {nl("  ").join(args_members)}
    366 
    367 public:
    368  LIR_HEADER({name})
    369 
    370  {nl("  ").join(oper_indices)}
    371 
    372  explicit {class_name}({", ".join(succ_params + oper_params + temp_params + args_params)}) : {parent_class}(classOpcode){", ".join([""] + args_initializers)} {{
    373    {constructor_init}
    374    {nl("    ").join(succ_initializers)}
    375    {nl("    ").join(oper_initializers)}
    376    {nl("    ").join(temp_initializers)}
    377  }}
    378 
    379  {nl("  ").join(succ_getters)}
    380  {nl("  ").join(oper_getters)}
    381  {nl("  ").join(oper_setters)}
    382  {nl("  ").join(temp_getters)}
    383  {nl("  ").join(temp_setters)}
    384  {nl("  ").join(args_getters)}
    385  {mir_accessor}
    386  {extra_name_decl}
    387 }};
    388 """
    389 
    390    # Remove blank lines and add backslashes at line endings.
    391    return "\\\n".join(line for line in code.splitlines() if line.strip())
    392 
    393 
    394 def mir_type_to_lir_type(mir_type):
    395    assert mir_type and mir_type != "None"
    396 
    397    if mir_type == "Value":
    398        return "BoxedValue"
    399 
    400    if mir_type == "Int64":
    401        return "Int64"
    402 
    403    return "WordSized"
    404 
    405 
    406 def generate_lir_header(c_out, yaml_path, mir_yaml_path):
    407    data = load_yaml(yaml_path)
    408 
    409    # LIR_OPCODE_LIST opcode.
    410    ops = []
    411 
    412    # Generated LIR op class definitions.
    413    lir_op_classes = []
    414 
    415    for op in data:
    416        name = op["name"]
    417 
    418        gen_boilerplate = op.get("gen_boilerplate", True)
    419        assert isinstance(gen_boilerplate, bool)
    420 
    421        if gen_boilerplate:
    422            result_type = op.get("result_type", None)
    423            assert result_type is None or result_type in result_types
    424 
    425            successors = op.get("successors", None)
    426            assert successors is None or isinstance(successors, list)
    427 
    428            operands = op.get("operands") or {}
    429            assert isinstance(operands, dict)
    430 
    431            arguments = op.get("arguments") or {}
    432            assert isinstance(arguments, dict)
    433 
    434            num_temps = op.get("num_temps", 0)
    435            assert isinstance(num_temps, int)
    436 
    437            num_temps64 = op.get("num_temps64", 0)
    438            assert isinstance(num_temps64, int)
    439 
    440            gen_boilerplate = op.get("gen_boilerplate", True)
    441            assert isinstance(gen_boilerplate, bool)
    442 
    443            call_instruction = op.get("call_instruction", None)
    444            assert isinstance(call_instruction, (type(None), bool))
    445 
    446            mir_op = op.get("mir_op", None)
    447            assert mir_op in (None, True) or isinstance(mir_op, str)
    448 
    449            extra_name = op.get("extra_name", False)
    450            assert isinstance(extra_name, bool)
    451 
    452            defer_init = op.get("defer_init", False)
    453            assert isinstance(defer_init, bool)
    454 
    455            lir_op_classes.append(
    456                gen_lir_class(
    457                    name,
    458                    result_type,
    459                    successors,
    460                    operands,
    461                    arguments,
    462                    num_temps,
    463                    num_temps64,
    464                    call_instruction,
    465                    mir_op,
    466                    extra_name,
    467                    defer_init,
    468                )
    469            )
    470 
    471        ops.append(f"_({name})")
    472 
    473    # Generate LIR instructions for MIR instructions with 'generate_lir': true
    474    mir_data = load_yaml(mir_yaml_path)
    475 
    476    for op in mir_data:
    477        name = op["name"]
    478 
    479        generate_lir = op.get("generate_lir", False)
    480        assert isinstance(generate_lir, bool)
    481 
    482        if generate_lir:
    483            # If the instruction has a `lir_result_type`, use it. If not, derive
    484            # the LIR result type from the MIR `result_type`.
    485            lir_result_type = op.get("lir_result_type", None)
    486            assert isinstance(lir_result_type, (type(None), str))
    487 
    488            result_type = op.get("result_type", None)
    489            assert isinstance(result_type, (type(None), str))
    490 
    491            if lir_result_type is not None:
    492                if lir_result_type == "none":
    493                    lir_result_type = None
    494                else:
    495                    assert lir_result_type in result_types
    496            elif result_type and result_type != "None":
    497                lir_result_type = mir_type_to_lir_type(result_type)
    498                assert lir_result_type in result_types
    499 
    500            successors = None
    501 
    502            operands_raw = op.get("operands", {})
    503            assert isinstance(operands_raw, dict)
    504 
    505            operands = {op: mir_type_to_lir_type(ty) for op, ty in operands_raw.items()}
    506 
    507            arguments = {}
    508 
    509            num_temps = op.get("lir_temps", 0)
    510            assert isinstance(num_temps, int)
    511 
    512            num_temps64 = op.get("lir_temps64", 0)
    513            assert isinstance(num_temps64, int)
    514 
    515            call_instruction = op.get("possibly_calls", None)
    516            assert isinstance(call_instruction, (type(None), bool))
    517 
    518            mir_op = True
    519 
    520            extra_name = False
    521 
    522            defer_init = False
    523 
    524            lir_op_classes.append(
    525                gen_lir_class(
    526                    name,
    527                    lir_result_type,
    528                    successors,
    529                    operands,
    530                    arguments,
    531                    num_temps,
    532                    num_temps64,
    533                    call_instruction,
    534                    mir_op,
    535                    extra_name,
    536                    defer_init,
    537                )
    538            )
    539 
    540            ops.append(f"_({name})")
    541 
    542    contents = "#define LIR_OPCODE_LIST(_)\\\n"
    543    contents += "\\\n".join(ops)
    544    contents += "\n\n"
    545 
    546    contents += "#define LIR_OPCODE_CLASS_GENERATED \\\n"
    547    contents += "\\\n".join(lir_op_classes)
    548    contents += "\n\n"
    549 
    550    generate_header(c_out, "jit_LIROpsGenerated_h", contents)