tor-browser

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

GenerateMIRFiles.py (14262B)


      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/MIROpsGenerated.h (list of MIR instructions)
      6 # from MIROps.yaml, as well as MIR op definitions.
      7 
      8 import io
      9 
     10 import buildconfig
     11 import yaml
     12 from mozbuild.preprocessor import Preprocessor
     13 
     14 HEADER_TEMPLATE = """\
     15 /* This Source Code Form is subject to the terms of the Mozilla Public
     16 * License, v. 2.0. If a copy of the MPL was not distributed with this
     17 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     18 
     19 #ifndef %(includeguard)s
     20 #define %(includeguard)s
     21 
     22 /* This file is generated by jit/GenerateMIRFiles.py. Do not edit! */
     23 
     24 %(contents)s
     25 
     26 #endif // %(includeguard)s
     27 """
     28 
     29 
     30 def generate_header(c_out, includeguard, contents):
     31    c_out.write(
     32        HEADER_TEMPLATE
     33        % {
     34            "includeguard": includeguard,
     35            "contents": contents,
     36        }
     37    )
     38 
     39 
     40 def load_yaml(yaml_path):
     41    # First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in
     42    # the YAML file.
     43    pp = Preprocessor()
     44    pp.context.update(buildconfig.defines["ALLDEFINES"])
     45    pp.out = io.StringIO()
     46    pp.do_filter("substitution")
     47    pp.do_include(yaml_path)
     48    contents = pp.out.getvalue()
     49    return yaml.safe_load(contents)
     50 
     51 
     52 type_policies = {
     53    "Object": "ObjectPolicy",
     54    "Value": "BoxPolicy",
     55    "Int32": "UnboxedInt32Policy",
     56    "BigInt": "BigIntPolicy",
     57    "IntPtr": "IntPtrPolicy",
     58    "Boolean": "BooleanPolicy",
     59    "Double": "DoublePolicy",
     60    "String": "StringPolicy",
     61    "Symbol": "SymbolPolicy",
     62    "NoTypePolicy": "NoTypePolicy",
     63    "Slots": "NoTypePolicy",
     64 }
     65 
     66 
     67 def decide_type_policy(types, no_type_policy):
     68    if no_type_policy:
     69        return "public NoTypePolicy::Data"
     70 
     71    type_num = 0
     72    mixed_type_policies = []
     73    for mir_type in types:
     74        policy = type_policies[mir_type]
     75        if policy == "NoTypePolicy":
     76            type_num += 1
     77            continue
     78        mixed_type_policies.append(f"{policy}<{type_num}>")
     79        type_num += 1
     80 
     81    if len(mixed_type_policies) == 0:
     82        return "public NoTypePolicy::Data"
     83 
     84    if len(mixed_type_policies) == 1:
     85        return f"public {mixed_type_policies[0]}::Data"
     86 
     87    return "public MixPolicy<{}>::Data".format(", ".join(mixed_type_policies))
     88 
     89 
     90 mir_base_class = [
     91    "MNullaryInstruction",
     92    "MUnaryInstruction",
     93    "MBinaryInstruction",
     94    "MTernaryInstruction",
     95    "MQuaternaryInstruction",
     96    "MQuinaryInstruction",
     97 ]
     98 
     99 
    100 gc_pointer_types = [
    101    "JSObject*",
    102    "NativeObject*",
    103    "JSFunction*",
    104    "BaseScript*",
    105    "PropertyName*",
    106    "Shape*",
    107    "GetterSetter*",
    108    "JSOffThreadAtom*",
    109    "ClassBodyScope*",
    110    "VarScope*",
    111    "NamedLambdaObject*",
    112    "RegExpObject*",
    113    "JSScript*",
    114    "LexicalScope*",
    115 ]
    116 
    117 special_storage_types = {
    118    "JSOffThreadAtom*": (
    119        "CompilerGCPointer<JSAtom*>",
    120        "{}->unwrap()",
    121        "&{}->asOffThreadAtom()",
    122    )
    123 }
    124 
    125 
    126 def arg_type_sig_to_init(type_sig, arg_name):
    127    if type_sig in special_types:
    128        _, init, _ = special_types[type_sig]
    129        return init.format(arg_name)
    130    else:
    131        return arg_name
    132 
    133 
    134 def gen_mir_class(
    135    name,
    136    operands,
    137    arguments,
    138    no_type_policy,
    139    result,
    140    guard,
    141    movable,
    142    folds_to,
    143    value_hash,
    144    congruent_to,
    145    alias_set,
    146    might_alias,
    147    possibly_calls,
    148    compute_range,
    149    can_recover,
    150    clone,
    151    can_consume_float32,
    152 ):
    153    """Generates class definition for a single MIR opcode."""
    154 
    155    # Generate a MIR opcode class definition.
    156    # For example:
    157    # class MGuardIndexIsValidUpdateOrAdd
    158    #     : public MBinaryInstruction,
    159    #       public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>>::Data {
    160    #  explicit MGuardIndexIsValidUpdateOrAdd(MDefinition* object,
    161    #                                         MDefinition* index)
    162    #     : MBinaryInstruction(classOpcode, object, index) {
    163    #   setGuard();
    164    #   setMovable();
    165    #   setResultType(MIRType::Int32);
    166    #  }
    167    # public:
    168    #  INSTRUCTION_HEADER(GetFrameArgument)
    169    #  TRIVIAL_NEW_WRAPPERS
    170    #  NAMED_OPERANDS((0, object), (1, index))
    171    #  AliasSet getAliasSet() const override { return AliasSet::None(); }
    172    #  bool congruentTo(const MDefinition* ins) const override {
    173    #    return congruentIfOperandsEqual(ins); }
    174    #  };
    175    #
    176 
    177    type_policy = ""
    178    # MIR op constructor operands.
    179    mir_operands = []
    180    # MIR op base class constructor operands.
    181    mir_base_class_operands = []
    182    # Types of each constructor operand.
    183    mir_types = []
    184    # Items for NAMED_OPERANDS.
    185    named_operands = []
    186    if operands:
    187        current_oper_num = 0
    188        for oper_name in operands:
    189            oper = "MDefinition* " + oper_name
    190            mir_operands.append(oper)
    191            mir_base_class_operands.append(", " + oper_name)
    192            # Collect all the MIR argument types to use for determining the
    193            # ops type policy.
    194            mir_types.append(operands[oper_name])
    195            # Collecting named operands for defining accessors.
    196            named_operands.append(f"({current_oper_num}, {oper_name})")
    197            current_oper_num += 1
    198        type_policy = decide_type_policy(mir_types, no_type_policy)
    199 
    200    class_name = "M" + name
    201 
    202    assert len(mir_operands) < 6
    203    base_class = mir_base_class[len(mir_operands)]
    204    assert base_class
    205    if base_class != "MNullaryInstruction":
    206        assert type_policy
    207        type_policy = ", " + type_policy
    208    code = f"class {class_name} : public {base_class}{type_policy} {{\\\n"
    209 
    210    # Arguments to class constructor that require accessors.
    211    mir_args = []
    212    if arguments:
    213        for arg_name in arguments:
    214            arg_type_sig = arguments[arg_name]
    215            mir_args.append(arg_type_sig + " " + arg_name)
    216            if arg_type_sig in special_storage_types:
    217                storage, _, _ = special_storage_types[arg_type_sig]
    218                code += "  " + storage
    219            elif arg_type_sig in gc_pointer_types:
    220                code += "  CompilerGCPointer<" + arg_type_sig + ">"
    221            else:
    222                code += "  " + arg_type_sig
    223            code += " " + arg_name + "_;\\\n"
    224 
    225    code += "  explicit {}({}) : {}(classOpcode{})".format(
    226        class_name,
    227        ", ".join(mir_operands + mir_args),
    228        base_class,
    229        "".join(mir_base_class_operands),
    230    )
    231    if arguments:
    232        for arg_name in arguments:
    233            code += ", " + arg_name + "_("
    234            arg_type_sig = arguments[arg_name]
    235            if arg_type_sig in special_storage_types:
    236                _, init, _ = special_storage_types[arg_type_sig]
    237                code += init.format(arg_name)
    238            else:
    239                code += arg_name
    240            code += ")"
    241    code += " {\\\n"
    242    if guard:
    243        code += "    setGuard();\\\n"
    244    if movable:
    245        code += "    setMovable();\\\n"
    246    # Note: MIRType::None is the default MIR result type so don't generate a
    247    # setResultType call for it.
    248    if result and result != "None":
    249        code += f"    setResultType(MIRType::{result});\\\n"
    250    code += "  }\\\n public:\\\n"
    251    if arguments:
    252        for arg_name in arguments:
    253            code += "  " + arguments[arg_name] + " " + arg_name + "() const { "
    254            arg_type_sig = arguments[arg_name]
    255            if arg_type_sig in special_storage_types:
    256                _, _, load = special_storage_types[arg_type_sig]
    257                code += "return " + load.format(arg_name + "_") + "; }\\\n"
    258            else:
    259                code += "return " + arg_name + "_; }\\\n"
    260    code += f"  INSTRUCTION_HEADER({name})\\\n"
    261    code += "  TRIVIAL_NEW_WRAPPERS\\\n"
    262    if named_operands:
    263        code += "  NAMED_OPERANDS({})\\\n".format(", ".join(named_operands))
    264    if alias_set:
    265        if alias_set == "custom":
    266            code += "  AliasSet getAliasSet() const override;\\\n"
    267        else:
    268            assert alias_set == "none"
    269            code += (
    270                "  AliasSet getAliasSet() const override { "
    271                "return AliasSet::None(); }\\\n"
    272            )
    273    if might_alias:
    274        code += "  AliasType mightAlias(const MDefinition* store) const override;\\\n"
    275    if folds_to:
    276        code += "  MDefinition* foldsTo(TempAllocator& alloc) override;\\\n"
    277    if congruent_to:
    278        if congruent_to == "custom":
    279            code += "  bool congruentTo(const MDefinition* ins) const override;\\\n"
    280        else:
    281            assert congruent_to == "if_operands_equal"
    282            code += (
    283                "  bool congruentTo(const MDefinition* ins) const override { "
    284                "return congruentIfOperandsEqual(ins); }\\\n"
    285            )
    286    if value_hash:
    287        assert value_hash == "custom"
    288        code += "  HashNumber valueHash() const override;\\\n"
    289    if possibly_calls:
    290        if possibly_calls == "custom":
    291            code += "  bool possiblyCalls() const override;\\\n"
    292        else:
    293            code += "  bool possiblyCalls() const override { return true; }\\\n"
    294    if compute_range:
    295        code += "  void computeRange(TempAllocator& alloc) override;\\\n"
    296    if can_recover:
    297        code += "  [[nodiscard]] bool writeRecoverData(\\\n"
    298        code += "    CompactBufferWriter& writer) const override;\\\n"
    299        if can_recover == "custom":
    300            code += "  bool canRecoverOnBailout() const override;\\\n"
    301        else:
    302            code += "  bool canRecoverOnBailout() const override { return true; }\\\n"
    303    if clone:
    304        code += "  ALLOW_CLONE(" + class_name + ")\\\n"
    305    if can_consume_float32:
    306        code += (
    307            "  bool canConsumeFloat32(MUse* use) const override { return true; }\\\n"
    308        )
    309    code += "};\\\n"
    310    return code
    311 
    312 
    313 def gen_non_gc_pointer_type_assertions(seen_types):
    314    """Generates a list of static assertions used to ensure that all argument
    315    types seen are not derived from gc::Cell, ensuring that gc pointer arguments
    316    are added to the gc_pointer_types list.
    317    """
    318    assertions = []
    319 
    320    for seen_type in sorted(seen_types):
    321        assertions.append(
    322            "static_assert(!std::is_base_of_v<gc::Cell, " + seen_type.strip("*") + ">, "
    323            '"Ensure that '
    324            + seen_type.strip("*")
    325            + ' is added to the gc_pointer_types list in GenerateMIRFiles.py."'
    326            ");"
    327        )
    328 
    329    return assertions
    330 
    331 
    332 def generate_mir_header(c_out, yaml_path):
    333    """Generate MIROpsGenerated.h from MIROps.yaml. The generated file
    334    has a list of MIR ops and boilerplate for MIR op definitions.
    335    """
    336 
    337    data = load_yaml(yaml_path)
    338 
    339    # MIR_OPCODE_LIST items. Stores the name of each MIR op.
    340    ops_items = []
    341 
    342    # Generated MIR op class definitions.
    343    mir_op_classes = []
    344 
    345    # Unique and non gc pointer types seen for arguments to the MIR constructor.
    346    seen_non_gc_pointer_argument_types = set()
    347 
    348    for op in data:
    349        name = op["name"]
    350 
    351        ops_items.append(f"_({name})")
    352 
    353        gen_boilerplate = op.get("gen_boilerplate", True)
    354        assert isinstance(gen_boilerplate, bool)
    355 
    356        if gen_boilerplate:
    357            operands = op.get("operands", None)
    358            assert operands is None or isinstance(operands, dict)
    359 
    360            arguments = op.get("arguments", None)
    361            assert arguments is None or isinstance(arguments, dict)
    362 
    363            no_type_policy = op.get("type_policy", None)
    364            assert no_type_policy in (None, "none")
    365 
    366            result = op.get("result_type", None)
    367            assert result is None or isinstance(result, str)
    368 
    369            guard = op.get("guard", None)
    370            assert guard in (None, True, False)
    371 
    372            movable = op.get("movable", None)
    373            assert movable in (None, True, False)
    374 
    375            folds_to = op.get("folds_to", None)
    376            assert folds_to in (None, "custom")
    377 
    378            value_hash = op.get("value_hash", None)
    379            assert value_hash in (None, "custom")
    380 
    381            congruent_to = op.get("congruent_to", None)
    382            assert congruent_to in (None, "if_operands_equal", "custom")
    383 
    384            alias_set = op.get("alias_set", None)
    385            assert alias_set in (None, "none", "custom")
    386 
    387            might_alias = op.get("might_alias", None)
    388            assert might_alias in (None, "custom")
    389 
    390            possibly_calls = op.get("possibly_calls", None)
    391            assert possibly_calls in (None, True, "custom")
    392 
    393            compute_range = op.get("compute_range", None)
    394            assert compute_range in (None, "custom")
    395 
    396            can_recover = op.get("can_recover", None)
    397            assert can_recover in (None, True, False, "custom")
    398 
    399            clone = op.get("clone", None)
    400            assert clone in (None, True, False)
    401 
    402            can_consume_float32 = op.get("can_consume_float32", None)
    403            assert can_consume_float32 in (None, True, False)
    404 
    405            code = gen_mir_class(
    406                name,
    407                operands,
    408                arguments,
    409                no_type_policy,
    410                result,
    411                guard,
    412                movable,
    413                folds_to,
    414                value_hash,
    415                congruent_to,
    416                alias_set,
    417                might_alias,
    418                possibly_calls,
    419                compute_range,
    420                can_recover,
    421                clone,
    422                can_consume_float32,
    423            )
    424            mir_op_classes.append(code)
    425 
    426            if arguments:
    427                for argument in arguments:
    428                    arg_type = arguments[argument]
    429                    if arg_type not in gc_pointer_types:
    430                        seen_non_gc_pointer_argument_types.add(arg_type)
    431 
    432    contents = "#define MIR_OPCODE_LIST(_)\\\n"
    433    contents += "\\\n".join(ops_items)
    434    contents += "\n\n"
    435 
    436    contents += "#define MIR_OPCODE_CLASS_GENERATED \\\n"
    437    contents += "\\\n".join(mir_op_classes)
    438    contents += "\n\n"
    439 
    440    contents += "#define NON_GC_POINTER_TYPE_ASSERTIONS_GENERATED \\\n"
    441    contents += "\\\n".join(
    442        gen_non_gc_pointer_type_assertions(seen_non_gc_pointer_argument_types)
    443    )
    444    contents += "\n\n"
    445 
    446    generate_header(c_out, "jit_MIROpsGenerated_h", contents)