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/LIROpsGenerated.h (list of LIR instructions)
# from LIROps.yaml.
import io
from itertools import groupby
from operator import itemgetter
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/GenerateLIRFiles.py. Do not edit! */
%(contents)s
#endif // %(includeguard)s
"""
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)
def generate_header(c_out, includeguard, contents):
c_out.write(
HEADER_TEMPLATE
% {
"includeguard": includeguard,
"contents": contents,
}
)
operand_types = {
"WordSized": "LAllocation",
"BoxedValue": "LBoxAllocation",
"Int64": "LInt64Allocation",
}
result_types = {
"WordSized": "1",
"BoxedValue": "BOX_PIECES",
"Int64": "INT64_PIECES",
}
# Generate the index expression for a BoxedValue operand.
#
# The expression has the form |num_operands + index * BOX_PIECES|, with zero
# terms being omitted.
def make_boxed_index(index, reg_operands):
num_operands = len(reg_operands)
expr = []
if num_operands:
expr.append(f"{num_operands}")
if index:
expr.append(f"{index} * BOX_PIECES")
return " + ".join(expr) if expr else "0"
# Generate the index expression for an Int64 operand.
#
# The expression has the form
# |num_operands + num_value_operands * BOX_PIECES + index * INT64_PIECES|, with
# zero terms being omitted.
def make_int64_index(index, reg_operands, value_operands):
num_operands = len(reg_operands)
num_value_operands = len(value_operands)
expr = []
if num_operands:
expr.append(f"{num_operands}")
if num_value_operands:
expr.append(f"{num_value_operands} * BOX_PIECES")
if index:
expr.append(f"{index} * INT64_PIECES")
return " + ".join(expr) if expr else "0"
def gen_operands(operands, defer_init):
# Group operands by operand type.
sorted_operands = {
k: [op for op, _ in v]
for k, v in groupby(sorted(operands.items(), key=itemgetter(1)), itemgetter(1))
}
# Exactly three operand types are supported: WordSized, BoxedValue, and Int64.
if len(sorted_operands) > 3:
raise Exception("Invalid operand type: " + str(sorted_operands.keys()))
reg_operands = sorted_operands.get("WordSized", [])
value_operands = sorted_operands.get("BoxedValue", [])
int64_operands = sorted_operands.get("Int64", [])
# Operand index definitions.
indices = []
# Parameters for the class constructor.
params = []
# Initializer instructions for constructor body.
initializers = []
# Getter definitions.
getters = []
# Setter definitions.
setters = []
# Constructor parameters are generated in the order defined in the YAML file.
if not defer_init:
for operand, op_type in operands.items():
params.append(f"const {operand_types[op_type]}& {operand}")
# First initialize all word-sized operands.
for index, operand in enumerate(reg_operands):
cap_operand = operand[0].upper() + operand[1:]
index_value = cap_operand + "Index"
init_expr = f"setOperand({index_value}, {operand});"
indices.append(f"static constexpr size_t {index_value} = {index};")
if not defer_init:
initializers.append(init_expr)
else:
setters.append(
f"void set{cap_operand}(const LAllocation& {operand}) {{ {init_expr} }}"
)
getters.append(
f"const LAllocation* {operand}() const {{ return getOperand({index_value}); }}"
)
# Next initialize all BoxedValue operands.
for box_index, operand in enumerate(value_operands):
cap_operand = operand[0].upper() + operand[1:]
index_value = cap_operand + "Index"
init_expr = f"setBoxOperand({index_value}, {operand});"
indices.append(
f"static constexpr size_t {index_value} = {make_boxed_index(box_index, reg_operands)};"
)
if not defer_init:
initializers.append(init_expr)
else:
setters.append(
f"void {cap_operand}(const LBoxAllocation& {operand}) {{ {init_expr} }}"
)
getters.append(
f"LBoxAllocation {operand}() const {{ return getBoxOperand({index_value}); }}"
)
# Finally initialize all Int64 operands.
for int64_index, operand in enumerate(int64_operands):
cap_operand = operand[0].upper() + operand[1:]
index_value = cap_operand + "Index"
init_expr = f"setInt64Operand({index_value}, {operand});"
indices.append(
f"static constexpr size_t {index_value} = {make_int64_index(int64_index, reg_operands, value_operands)};"
)
if not defer_init:
initializers.append(init_expr)
else:
setters.append(
f"void set{cap_operand}(const LInt64Allocation& {operand}) {{ {init_expr} }}"
)
getters.append(
f"LInt64Allocation {operand}() const {{ return getInt64Operand({index_value}); }}"
)
# Total number of operands.
num_operands = f"{len(reg_operands)}"
if value_operands:
num_operands += f" + {len(value_operands)} * BOX_PIECES"
if int64_operands:
num_operands += f" + {len(int64_operands)} * INT64_PIECES"
return (
num_operands,
indices,
params,
initializers,
getters,
setters,
)
def gen_arguments(arguments):
# Class member definitions.
members = []
# Parameters for the class constructor.
params = []
# Initializer instructions for the class constructor.
initializers = []
# Getter definitions.
getters = []
for arg_name in arguments:
arg_type_sig = arguments[arg_name]
members.append(f"{arg_type_sig} {arg_name}_;")
params.append(f"{arg_type_sig} {arg_name}")
initializers.append(f"{arg_name}_({arg_name})")
getters.append(f"{arg_type_sig} {arg_name}() const {{ return {arg_name}_; }}")
return (members, params, initializers, getters)
def gen_temps(num_temps, num_temps64, defer_init):
# Parameters for the class constructor.
params = []
# Initializer instructions for constructor body.
initializers = []
# Getter definitions.
getters = []
# Setter definitions.
setters = []
for temp in range(num_temps):
param_decl = f"const LDefinition& temp{temp}"
init_expr = f"setTemp({temp}, temp{temp});"
if not defer_init:
params.append(param_decl)
initializers.append(init_expr)
else:
initializers.append(f"setTemp({temp}, LDefinition::BogusTemp());")
setters.append(f"void setTemp{temp}({param_decl}) {{ {init_expr} }}")
getters.append(f"const LDefinition* temp{temp}() {{ return getTemp({temp}); }}")
for int64_temp in range(num_temps64):
temp = num_temps + int64_temp
temp_index = f"{num_temps} + {int64_temp} * INT64_PIECES"
param_decl = f"const LInt64Definition& temp{temp}"
init_expr = f"setInt64Temp({temp_index}, temp{temp});"
if not defer_init:
params.append(param_decl)
initializers.append(init_expr)
else:
initializers.append(f"setTemp({temp}, LInt64Definition::BogusTemp());")
setters.append(f"void setTemp{temp}({param_decl}) {{ {init_expr} }}")
getters.append(
f"LInt64Definition temp{temp}() {{ return getInt64Temp({temp_index}); }}"
)
# Total number of temps.
num_temps_total = f"{num_temps}"
if num_temps64:
num_temps_total += f" + {num_temps64} * INT64_PIECES"
return (num_temps_total, params, initializers, getters, setters)
def gen_successors(successors):
# Parameters for the class constructor.
params = []
# Initializer instructions for constructor body.
initializers = []
# Getter definitions.
getters = []
for index, successor in enumerate(successors or []):
params.append(f"MBasicBlock* {successor}")
initializers.append(f"setSuccessor({index}, {successor});")
getters.append(
f"MBasicBlock* {successor}() const {{ return getSuccessor({index}); }}"
)
return (params, initializers, getters)
def gen_lir_class(
name,
result_type,
successors,
operands,
arguments,
num_temps,
num_temps64,
call_instruction,
mir_op,
extra_name,
defer_init,
):
"""Generates class definition for a single LIR opcode."""
class_name = "L" + name
(
num_operands,
oper_indices,
oper_params,
oper_initializers,
oper_getters,
oper_setters,
) = gen_operands(operands, defer_init)
args_members, args_params, args_initializers, args_getters = gen_arguments(
arguments
)
num_temps_total, temp_params, temp_initializers, temp_getters, temp_setters = (
gen_temps(num_temps, num_temps64, defer_init)
)
succ_params, succ_initializers, succ_getters = gen_successors(successors)
if successors is not None:
if result_type:
raise Exception("Control instructions don't return a result")
num_defs = len(successors)
parent_class = "LControlInstructionHelper"
else:
num_defs = result_types[result_type] if result_type else "0"
parent_class = "LInstructionHelper"
constructor_init = ""
if call_instruction:
constructor_init += "this->setIsCall();"
mir_accessor = ""
if mir_op:
mir_name = name if mir_op is True else mir_op
mir_accessor = f"M{mir_name}* mir() const {{ return mir_->to{mir_name}(); }};"
extra_name_decl = ""
if extra_name:
extra_name_decl = "inline const char* extraName() const;"
# Can be moved into the f-string when we use Python 3.12, see PEP 701.
def nl(ws):
return "\n" + ws
code = f"""
class {class_name} : public {parent_class}<{num_defs}, {num_operands}, {num_temps_total}> {{
{nl(" ").join(args_members)}
public:
LIR_HEADER({name})
{nl(" ").join(oper_indices)}
explicit {class_name}({", ".join(succ_params + oper_params + temp_params + args_params)}) : {parent_class}(classOpcode){", ".join([""] + args_initializers)} {{
{constructor_init}
{nl(" ").join(succ_initializers)}
{nl(" ").join(oper_initializers)}
{nl(" ").join(temp_initializers)}
}}
{nl(" ").join(succ_getters)}
{nl(" ").join(oper_getters)}
{nl(" ").join(oper_setters)}
{nl(" ").join(temp_getters)}
{nl(" ").join(temp_setters)}
{nl(" ").join(args_getters)}
{mir_accessor}
{extra_name_decl}
}};
"""
# Remove blank lines and add backslashes at line endings.
return "\\\n".join(line for line in code.splitlines() if line.strip())
def mir_type_to_lir_type(mir_type):
if mir_type == "Value":
return "BoxedValue"
return "WordSized"
def generate_lir_header(c_out, yaml_path, mir_yaml_path):
data = load_yaml(yaml_path)
# LIR_OPCODE_LIST opcode.
ops = []
# Generated LIR op class definitions.
lir_op_classes = []
for op in data:
name = op["name"]
gen_boilerplate = op.get("gen_boilerplate", True)
assert isinstance(gen_boilerplate, bool)
if gen_boilerplate:
result_type = op.get("result_type", None)
assert result_type is None or result_type in result_types
successors = op.get("successors", None)
assert successors is None or isinstance(successors, list)
operands = op.get("operands") or {}
assert isinstance(operands, dict)
arguments = op.get("arguments") or {}
assert isinstance(arguments, dict)
num_temps = op.get("num_temps", 0)
assert isinstance(num_temps, int)
num_temps64 = op.get("num_temps64", 0)
assert isinstance(num_temps64, int)
gen_boilerplate = op.get("gen_boilerplate", True)
assert isinstance(gen_boilerplate, bool)
call_instruction = op.get("call_instruction", None)
assert isinstance(call_instruction, (type(None), bool))
mir_op = op.get("mir_op", None)
assert mir_op in (None, True) or isinstance(mir_op, str)
extra_name = op.get("extra_name", False)
assert isinstance(extra_name, bool)
defer_init = op.get("defer_init", False)
assert isinstance(defer_init, bool)
lir_op_classes.append(
gen_lir_class(
name,
result_type,
successors,
operands,
arguments,
num_temps,
num_temps64,
call_instruction,
mir_op,
extra_name,
defer_init,
)
)
ops.append("_({})".format(name))
# Generate LIR instructions for MIR instructions with 'generate_lir': true
mir_data = load_yaml(mir_yaml_path)
for op in mir_data:
name = op["name"]
generate_lir = op.get("generate_lir", False)
assert isinstance(generate_lir, bool)
if generate_lir:
result_type = op.get("result_type", None)
assert isinstance(result_type, (type(None), str))
if result_type:
result_type = mir_type_to_lir_type(result_type)
assert result_type in result_types
successors = None
operands_raw = op.get("operands", {})
assert isinstance(operands_raw, dict)
operands = {op: mir_type_to_lir_type(ty) for op, ty in operands_raw.items()}
arguments = {}
num_temps = op.get("lir_temps", 0)
assert isinstance(num_temps, int)
num_temps64 = op.get("lir_temps64", 0)
assert isinstance(num_temps64, int)
call_instruction = op.get("possibly_calls", None)
assert isinstance(call_instruction, (type(None), bool))
mir_op = True
extra_name = False
defer_init = False
lir_op_classes.append(
gen_lir_class(
name,
result_type,
successors,
operands,
arguments,
num_temps,
num_temps64,
call_instruction,
mir_op,
extra_name,
defer_init,
)
)
ops.append("_({})".format(name))
contents = "#define LIR_OPCODE_LIST(_)\\\n"
contents += "\\\n".join(ops)
contents += "\n\n"
contents += "#define LIR_OPCODE_CLASS_GENERATED \\\n"
contents += "\\\n".join(lir_op_classes)
contents += "\n\n"
generate_header(c_out, "jit_LIROpsGenerated_h", contents)