forked from mirrors/gecko-dev
`mach` requires Python 3.7+ since bug 1734402, and Python 3.7 made preserving dictionary insertion order an official part of the language. Also use `safe_load` instead of `load` because it doesn't require a loader argument and is safer (although it doesn't really matter for this use case). Differential Revision: https://phabricator.services.mozilla.com/D197497
394 lines
12 KiB
Python
394 lines
12 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
# This script generates jit/MIROpsGenerated.h (list of MIR instructions)
|
|
# from MIROps.yaml, as well as MIR op definitions.
|
|
|
|
import buildconfig
|
|
import six
|
|
import yaml
|
|
from mozbuild.preprocessor import Preprocessor
|
|
|
|
HEADER_TEMPLATE = """\
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef %(includeguard)s
|
|
#define %(includeguard)s
|
|
|
|
/* This file is generated by jit/GenerateMIRFiles.py. Do not edit! */
|
|
|
|
%(contents)s
|
|
|
|
#endif // %(includeguard)s
|
|
"""
|
|
|
|
|
|
def generate_header(c_out, includeguard, contents):
|
|
c_out.write(
|
|
HEADER_TEMPLATE
|
|
% {
|
|
"includeguard": includeguard,
|
|
"contents": contents,
|
|
}
|
|
)
|
|
|
|
|
|
def load_yaml(yaml_path):
|
|
# First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in
|
|
# the YAML file.
|
|
pp = Preprocessor()
|
|
pp.context.update(buildconfig.defines["ALLDEFINES"])
|
|
pp.out = six.StringIO()
|
|
pp.do_filter("substitution")
|
|
pp.do_include(yaml_path)
|
|
contents = pp.out.getvalue()
|
|
return yaml.safe_load(contents)
|
|
|
|
|
|
type_policies = {
|
|
"Object": "ObjectPolicy",
|
|
"Value": "BoxPolicy",
|
|
"Int32": "UnboxedInt32Policy",
|
|
"BigInt": "BigIntPolicy",
|
|
"Boolean": "BooleanPolicy",
|
|
"Double": "DoublePolicy",
|
|
"String": "StringPolicy",
|
|
"Symbol": "SymbolPolicy",
|
|
}
|
|
|
|
|
|
def decide_type_policy(types, no_type_policy):
|
|
if no_type_policy:
|
|
return "public NoTypePolicy::Data"
|
|
|
|
if len(types) == 1:
|
|
return "public {}<0>::Data".format(type_policies[types[0]])
|
|
|
|
type_num = 0
|
|
mixed_type_policies = []
|
|
for mir_type in types:
|
|
policy = type_policies[mir_type]
|
|
mixed_type_policies.append("{}<{}>".format(policy, type_num))
|
|
type_num += 1
|
|
|
|
return "public MixPolicy<{}>::Data".format(", ".join(mixed_type_policies))
|
|
|
|
|
|
mir_base_class = [
|
|
"MNullaryInstruction",
|
|
"MUnaryInstruction",
|
|
"MBinaryInstruction",
|
|
"MTernaryInstruction",
|
|
"MQuaternaryInstruction",
|
|
]
|
|
|
|
|
|
gc_pointer_types = [
|
|
"JSObject*",
|
|
"NativeObject*",
|
|
"JSFunction*",
|
|
"BaseScript*",
|
|
"PropertyName*",
|
|
"Shape*",
|
|
"GetterSetter*",
|
|
"JSAtom*",
|
|
"ClassBodyScope*",
|
|
"VarScope*",
|
|
"NamedLambdaObject*",
|
|
"RegExpObject*",
|
|
"JSScript*",
|
|
"LexicalScope*",
|
|
]
|
|
|
|
|
|
def gen_mir_class(
|
|
name,
|
|
operands,
|
|
arguments,
|
|
no_type_policy,
|
|
result,
|
|
guard,
|
|
movable,
|
|
folds_to,
|
|
congruent_to,
|
|
alias_set,
|
|
might_alias,
|
|
possibly_calls,
|
|
compute_range,
|
|
can_recover,
|
|
clone,
|
|
can_consume_float32,
|
|
):
|
|
"""Generates class definition for a single MIR opcode."""
|
|
|
|
# Generate a MIR opcode class definition.
|
|
# For example:
|
|
# class MGuardIndexIsValidUpdateOrAdd
|
|
# : public MBinaryInstruction,
|
|
# public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>>::Data {
|
|
# explicit MGuardIndexIsValidUpdateOrAdd(MDefinition* object,
|
|
# MDefinition* index)
|
|
# : MBinaryInstruction(classOpcode, object, index) {
|
|
# setGuard();
|
|
# setMovable();
|
|
# setResultType(MIRType::Int32);
|
|
# }
|
|
# public:
|
|
# INSTRUCTION_HEADER(GetFrameArgument)
|
|
# TRIVIAL_NEW_WRAPPERS
|
|
# NAMED_OPERANDS((0, object), (1, index))
|
|
# AliasSet getAliasSet() const override { return AliasSet::None(); }
|
|
# bool congruentTo(const MDefinition* ins) const override {
|
|
# return congruentIfOperandsEqual(ins); }
|
|
# };
|
|
#
|
|
|
|
type_policy = ""
|
|
# MIR op constructor operands.
|
|
mir_operands = []
|
|
# MIR op base class constructor operands.
|
|
mir_base_class_operands = []
|
|
# Types of each constructor operand.
|
|
mir_types = []
|
|
# Items for NAMED_OPERANDS.
|
|
named_operands = []
|
|
if operands:
|
|
current_oper_num = 0
|
|
for oper_name in operands:
|
|
oper = "MDefinition* " + oper_name
|
|
mir_operands.append(oper)
|
|
mir_base_class_operands.append(", " + oper_name)
|
|
# Collect all the MIR argument types to use for determining the
|
|
# ops type policy.
|
|
mir_types.append(operands[oper_name])
|
|
# Collecting named operands for defining accessors.
|
|
named_operands.append("({}, {})".format(current_oper_num, oper_name))
|
|
current_oper_num += 1
|
|
type_policy = decide_type_policy(mir_types, no_type_policy)
|
|
|
|
class_name = "M" + name
|
|
|
|
assert len(mir_operands) < 5
|
|
base_class = mir_base_class[len(mir_operands)]
|
|
assert base_class
|
|
if base_class != "MNullaryInstruction":
|
|
assert type_policy
|
|
type_policy = ", " + type_policy
|
|
code = "class {} : public {}{} {{\\\n".format(class_name, base_class, type_policy)
|
|
|
|
# Arguments to class constructor that require accessors.
|
|
mir_args = []
|
|
if arguments:
|
|
for arg_name in arguments:
|
|
arg_type_sig = arguments[arg_name]
|
|
mir_args.append(arg_type_sig + " " + arg_name)
|
|
if arg_type_sig in gc_pointer_types:
|
|
code += " CompilerGCPointer<" + arg_type_sig + ">"
|
|
else:
|
|
code += " " + arg_type_sig
|
|
code += " " + arg_name + "_;\\\n"
|
|
|
|
code += " explicit {}({}) : {}(classOpcode{})".format(
|
|
class_name,
|
|
", ".join(mir_operands + mir_args),
|
|
base_class,
|
|
"".join(mir_base_class_operands),
|
|
)
|
|
if arguments:
|
|
for arg_name in arguments:
|
|
code += ", " + arg_name + "_(" + arg_name + ")"
|
|
code += " {\\\n"
|
|
if guard:
|
|
code += " setGuard();\\\n"
|
|
if movable:
|
|
code += " setMovable();\\\n"
|
|
if result:
|
|
code += " setResultType(MIRType::{});\\\n".format(result)
|
|
code += " }\\\n public:\\\n"
|
|
if arguments:
|
|
for arg_name in arguments:
|
|
code += " " + arguments[arg_name] + " " + arg_name + "() const { "
|
|
code += "return " + arg_name + "_; }\\\n"
|
|
code += " INSTRUCTION_HEADER({})\\\n".format(name)
|
|
code += " TRIVIAL_NEW_WRAPPERS\\\n"
|
|
if named_operands:
|
|
code += " NAMED_OPERANDS({})\\\n".format(", ".join(named_operands))
|
|
if alias_set:
|
|
if alias_set == "custom":
|
|
code += " AliasSet getAliasSet() const override;\\\n"
|
|
else:
|
|
assert alias_set == "none"
|
|
code += (
|
|
" AliasSet getAliasSet() const override { "
|
|
"return AliasSet::None(); }\\\n"
|
|
)
|
|
if might_alias:
|
|
code += " AliasType mightAlias(const MDefinition* store) const override;\\\n"
|
|
if folds_to:
|
|
code += " MDefinition* foldsTo(TempAllocator& alloc) override;\\\n"
|
|
if congruent_to:
|
|
if congruent_to == "custom":
|
|
code += " bool congruentTo(const MDefinition* ins) const override;\\\n"
|
|
else:
|
|
assert congruent_to == "if_operands_equal"
|
|
code += (
|
|
" bool congruentTo(const MDefinition* ins) const override { "
|
|
"return congruentIfOperandsEqual(ins); }\\\n"
|
|
)
|
|
if possibly_calls:
|
|
if possibly_calls == "custom":
|
|
code += " bool possiblyCalls() const override;\\\n"
|
|
else:
|
|
code += " bool possiblyCalls() const override { return true; }\\\n"
|
|
if compute_range:
|
|
code += " void computeRange(TempAllocator& alloc) override;\\\n"
|
|
if can_recover:
|
|
code += " [[nodiscard]] bool writeRecoverData(\\\n"
|
|
code += " CompactBufferWriter& writer) const override;\\\n"
|
|
if can_recover == "custom":
|
|
code += " bool canRecoverOnBailout() const override;\\\n"
|
|
else:
|
|
code += " bool canRecoverOnBailout() const override { return true; }\\\n"
|
|
if clone:
|
|
code += " ALLOW_CLONE(" + class_name + ")\\\n"
|
|
if can_consume_float32:
|
|
code += (
|
|
" bool canConsumeFloat32(MUse* use) const override { return true; }\\\n"
|
|
)
|
|
code += "};\\\n"
|
|
return code
|
|
|
|
|
|
def gen_non_gc_pointer_type_assertions(seen_types):
|
|
"""Generates a list of static assertions used to ensure that all argument
|
|
types seen are not derived from gc::Cell, ensuring that gc pointer arguments
|
|
are added to the gc_pointer_types list.
|
|
"""
|
|
assertions = []
|
|
|
|
for seen_type in sorted(seen_types):
|
|
assertions.append(
|
|
"static_assert(!std::is_base_of_v<gc::Cell, " + seen_type.strip("*") + ">, "
|
|
'"Ensure that '
|
|
+ seen_type.strip("*")
|
|
+ ' is added to the gc_pointer_types list in GenerateMIRFiles.py."'
|
|
");"
|
|
)
|
|
|
|
return assertions
|
|
|
|
|
|
def generate_mir_header(c_out, yaml_path):
|
|
"""Generate MIROpsGenerated.h from MIROps.yaml. The generated file
|
|
has a list of MIR ops and boilerplate for MIR op definitions.
|
|
"""
|
|
|
|
data = load_yaml(yaml_path)
|
|
|
|
# MIR_OPCODE_LIST items. Stores the name of each MIR op.
|
|
ops_items = []
|
|
|
|
# Generated MIR op class definitions.
|
|
mir_op_classes = []
|
|
|
|
# Unique and non gc pointer types seen for arguments to the MIR constructor.
|
|
seen_non_gc_pointer_argument_types = set()
|
|
|
|
for op in data:
|
|
name = op["name"]
|
|
|
|
ops_items.append("_({})".format(name))
|
|
|
|
gen_boilerplate = op.get("gen_boilerplate", True)
|
|
assert isinstance(gen_boilerplate, bool)
|
|
|
|
if gen_boilerplate:
|
|
operands = op.get("operands", None)
|
|
assert operands is None or isinstance(operands, dict)
|
|
|
|
arguments = op.get("arguments", None)
|
|
assert arguments is None or isinstance(arguments, dict)
|
|
|
|
no_type_policy = op.get("type_policy", None)
|
|
assert no_type_policy in (None, "none")
|
|
|
|
result = op.get("result_type", None)
|
|
assert result is None or isinstance(result, str)
|
|
|
|
guard = op.get("guard", None)
|
|
assert guard in (None, True, False)
|
|
|
|
movable = op.get("movable", None)
|
|
assert movable in (None, True, False)
|
|
|
|
folds_to = op.get("folds_to", None)
|
|
assert folds_to in (None, "custom")
|
|
|
|
congruent_to = op.get("congruent_to", None)
|
|
assert congruent_to in (None, "if_operands_equal", "custom")
|
|
|
|
alias_set = op.get("alias_set", None)
|
|
assert alias_set in (None, "none", "custom")
|
|
|
|
might_alias = op.get("might_alias", None)
|
|
assert might_alias in (None, "custom")
|
|
|
|
possibly_calls = op.get("possibly_calls", None)
|
|
assert possibly_calls in (None, True, "custom")
|
|
|
|
compute_range = op.get("compute_range", None)
|
|
assert compute_range in (None, "custom")
|
|
|
|
can_recover = op.get("can_recover", None)
|
|
assert can_recover in (None, True, False, "custom")
|
|
|
|
clone = op.get("clone", None)
|
|
assert clone in (None, True, False)
|
|
|
|
can_consume_float32 = op.get("can_consume_float32", None)
|
|
assert can_consume_float32 in (None, True, False)
|
|
|
|
code = gen_mir_class(
|
|
name,
|
|
operands,
|
|
arguments,
|
|
no_type_policy,
|
|
result,
|
|
guard,
|
|
movable,
|
|
folds_to,
|
|
congruent_to,
|
|
alias_set,
|
|
might_alias,
|
|
possibly_calls,
|
|
compute_range,
|
|
can_recover,
|
|
clone,
|
|
can_consume_float32,
|
|
)
|
|
mir_op_classes.append(code)
|
|
|
|
if arguments:
|
|
for argument in arguments:
|
|
arg_type = arguments[argument]
|
|
if arg_type not in gc_pointer_types:
|
|
seen_non_gc_pointer_argument_types.add(arg_type)
|
|
|
|
contents = "#define MIR_OPCODE_LIST(_)\\\n"
|
|
contents += "\\\n".join(ops_items)
|
|
contents += "\n\n"
|
|
|
|
contents += "#define MIR_OPCODE_CLASS_GENERATED \\\n"
|
|
contents += "\\\n".join(mir_op_classes)
|
|
contents += "\n\n"
|
|
|
|
contents += "#define NON_GC_POINTER_TYPE_ASSERTIONS_GENERATED \\\n"
|
|
contents += "\\\n".join(
|
|
gen_non_gc_pointer_type_assertions(seen_non_gc_pointer_argument_types)
|
|
)
|
|
contents += "\n\n"
|
|
|
|
generate_header(c_out, "jit_MIROpsGenerated_h", contents)
|