Source code

Revision control

Copy as Markdown

Other Tools

# 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)