# 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 io
import buildconfig
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 = io.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" ,
"ValueOrObject" :
"BoxExceptObjectPolicy" ,
"Int32" :
"UnboxedInt32Policy" ,
"BigInt" :
"BigIntPolicy" ,
"IntPtr" :
"IntPtrPolicy" ,
"Boolean" :
"BooleanPolicy" ,
"Double" :
"DoublePolicy" ,
"String" :
"StringPolicy" ,
"Symbol" :
"SymbolPolicy" ,
"Slots" :
"NoTypePolicy" ,
"any" :
"NoTypePolicy" ,
}
def decide_type_policy(types, no_type_policy):
if no_type_policy:
return "public NoTypePolicy::Data"
mixed_type_policies = []
for type_num, mir_type
in enumerate(types):
policy = type_policies[mir_type]
if policy !=
"NoTypePolicy" :
mixed_type_policies.append(f
"{policy}<{type_num}>" )
if len(mixed_type_policies) ==
0 :
return "public NoTypePolicy::Data"
if len(mixed_type_policies) ==
1 :
return f
"public {mixed_type_policies[0]}::Data"
return "public MixPolicy<{}>::Data" .format(
", " .join(mixed_type_policies))
# Alias set flags defined in MIR.h.
alias_set_flags = {
"None" ,
"ObjectFields" ,
"Element" ,
"UnboxedElement" ,
"DynamicSlot" ,
"FixedSlot" ,
"DOMProperty" ,
"WasmInstanceData" ,
"WasmHeap" ,
"WasmHeapMeta" ,
"ArrayBufferViewLengthOrOffset" ,
"WasmGlobalCell" ,
"WasmTableElement" ,
"WasmTableMeta" ,
"WasmStackResult" ,
"ExceptionState" ,
"DOMProxyExpando" ,
"MapOrSetHashTable" ,
"RNG" ,
"WasmPendingException" ,
"FuzzilliHash" ,
"WasmStructInlineDataArea" ,
"WasmStructOutlineDataPointer" ,
"WasmStructOutlineDataArea" ,
"WasmArrayNumElements" ,
"WasmArrayDataPointer" ,
"WasmArrayDataArea" ,
"GlobalGenerationCounter" ,
"SharedArrayRawBufferLength" ,
"Any" ,
}
def get_alias_set(load_or_store, alias_set):
assert load_or_store
in (
"load" ,
"store" )
flags = alias_set.get(load_or_store, [])
if isinstance(flags, str):
flags = [flags]
assert isinstance(flags, list)
assert alias_set_flags.issuperset(flags),
"unknown alias set flag: " + str(flags)
assert len(flags) == len(set(flags)),
"unexpected duplicates: " + str(flags)
# `AliasSet::{Load,Store}(AliasSet::None)` is not valid, so handle it early.
if len(flags) ==
0 or flags[
0 ] ==
"None" :
return None
expr =
" | " .join(f
"AliasSet::{flag}" for flag
in flags)
return f
"AliasSet::{load_or_store.title()}({expr})"
mir_base_class = [
"MNullaryInstruction" ,
"MUnaryInstruction" ,
"MBinaryInstruction" ,
"MTernaryInstruction" ,
"MQuaternaryInstruction" ,
"MQuinaryInstruction" ,
]
gc_pointer_types = [
"JSObject*" ,
"NativeObject*" ,
"JSFunction*" ,
"BaseScript*" ,
"PropertyName*" ,
"Shape*" ,
"GetterSetter*" ,
"JSOffThreadAtom*" ,
"ClassBodyScope*" ,
"VarScope*" ,
"NamedLambdaObject*" ,
"RegExpObject*" ,
"JSScript*" ,
"LexicalScope*" ,
"ArgumentsObject*" ,
]
special_storage_types = {
"JSOffThreadAtom*" : (
"CompilerGCPointer<JSAtom*>" ,
"{}->unwrap()" ,
"&{}->asOffThreadAtom()" ,
)
}
def arg_type_sig_to_init(type_sig, arg_name):
if type_sig
in special_types:
_, init, _ = special_types[type_sig]
return init.format(arg_name)
else :
return arg_name
def gen_mir_class(
name,
operands,
arguments,
no_type_policy,
result,
guard,
movable,
folds_to,
value_hash,
congruent_to,
alias_set,
might_alias,
possibly_calls,
compute_range,
can_recover,
clone,
can_consume_float32,
wasm_ref_type,
):
"" "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:
for current_oper_num, oper_name
in enumerate(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(f
"({current_oper_num}, {oper_name})" )
type_policy = decide_type_policy(mir_types, no_type_policy)
class_name =
"M" + name
assert len(mir_operands) <
6
base_class = mir_base_class[len(mir_operands)]
assert base_class
if base_class !=
"MNullaryInstruction" :
assert type_policy
type_policy =
", " + type_policy
code = f
"class {class_name} : public {base_class}{type_policy} {{\\\n"
# 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 special_storage_types:
storage, _, _ = special_storage_types[arg_type_sig]
code +=
" " + storage
elif 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_type_sig = arguments[arg_name]
if arg_type_sig
in special_storage_types:
_, init, _ = special_storage_types[arg_type_sig]
code += init.format(arg_name)
else :
code += arg_name
code +=
")"
code +=
" {\\\n"
if operands:
for oper_name
in operands:
mir_type = operands[oper_name]
# Skip over operands which can have any type.
if mir_type ==
"any" :
continue
policy = type_policies.get(mir_type,
"NoTypePolicy" )
if no_type_policy
or policy ==
"NoTypePolicy" :
code += (
f
" MOZ_ASSERT({oper_name}->type() == MIRType::{mir_type});\\\n"
)
if guard:
code +=
" setGuard();\\\n"
if movable:
code +=
" setMovable();\\\n"
if wasm_ref_type
is not None :
code += f
" setWasmRefType({wasm_ref_type});\\\n"
# Note: MIRType::None is the default MIR result type so don't generate a
# setResultType call for it.
if result
and result !=
"None" :
code += f
" setResultType(MIRType::{result});\\\n"
code +=
" }\\\n public:\\\n"
if arguments:
for arg_name
in arguments:
code +=
" " + arguments[arg_name] +
" " + arg_name +
"() const { "
arg_type_sig = arguments[arg_name]
if arg_type_sig
in special_storage_types:
_, _, load = special_storage_types[arg_type_sig]
code +=
"return " + load.format(arg_name +
"_" ) +
"; }\\\n"
else :
code +=
"return " + arg_name +
"_; }\\\n"
code += f
" INSTRUCTION_HEADER({name})\\\n"
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"
elif alias_set ==
"none" :
code += (
" AliasSet getAliasSet() const override { "
"return AliasSet::None(); }\\\n"
)
else :
assert isinstance(alias_set, dict)
assert {
"load" ,
"store" }.issuperset(alias_set.keys())
load = get_alias_set(
"load" , alias_set)
store = get_alias_set(
"store" , alias_set)
if load
and store:
expr = f
"{load} | {store}"
elif load
or store:
expr = load
if load
else store
else :
expr =
"AliasSet::None()"
code += f
" AliasSet getAliasSet() const override {{ return {expr}; }}\\\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
in (
"if_operands_equal" ,
"if_operands_equal_ignore_arguments" ,
"if_operands_and_arguments_equal" ,
)
assert not arguments
or congruent_to
in (
"if_operands_equal_ignore_arguments" ,
"if_operands_and_arguments_equal" ,
), (
f
"can't request congruent_to = 'if_operands_equal' for {name} because the instruction has arguments"
)
operands_and_args = [
"congruentIfOperandsEqual(ins)" ]
if arguments
and congruent_to ==
"if_operands_and_arguments_equal" :
for arg_name
in arguments:
operands_and_args.append(
f
"ins->to{name}()->{arg_name}_ == {arg_name}_"
)
expr =
" && " .join(operands_and_args)
code += (
" bool congruentTo(const MDefinition* ins) const override { "
f
"return {expr}; }}\\\n"
)
if value_hash:
assert value_hash ==
"custom"
code +=
" HashNumber valueHash() const override;\\\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(f
"_({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" )
value_hash = op.get(
"value_hash" ,
None )
assert value_hash
in (
None ,
"custom" )
congruent_to = op.get(
"congruent_to" ,
None )
assert congruent_to
in (
None ,
"if_operands_equal" ,
"if_operands_equal_ignore_arguments" ,
"if_operands_and_arguments_equal" ,
"custom" ,
)
alias_set = op.get(
"alias_set" ,
None )
assert alias_set
in (
None ,
"none" ,
"custom" )
or isinstance(alias_set, dict)
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 )
wasm_ref_type = op.get(
"wasm_ref_type" ,
None )
assert result
is None or isinstance(result, str)
code = gen_mir_class(
name,
operands,
arguments,
no_type_policy,
result,
guard,
movable,
folds_to,
value_hash,
congruent_to,
alias_set,
might_alias,
possibly_calls,
compute_range,
can_recover,
clone,
can_consume_float32,
wasm_ref_type,
)
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)
Messung V0.5 in Prozent C=91 H=92 G=91
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-25)
¤
*© Formatika GbR, Deutschland