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/LIROpsGenerated.h (list of LIR instructions)
6# from LIROps.yaml.
7
8import buildconfig
9import yaml
10import six
11from collections import OrderedDict
12from mozbuild.preprocessor import Preprocessor
13
14HEADER_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/GenerateLIRFiles.py. Do not edit! */
23
24%(contents)s
25
26#endif // %(includeguard)s
27"""
28
29
30def load_yaml(yaml_path):
31    # First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in
32    # the YAML file.
33    pp = Preprocessor()
34    pp.context.update(buildconfig.defines["ALLDEFINES"])
35    pp.out = six.StringIO()
36    pp.do_filter("substitution")
37    pp.do_include(yaml_path)
38    contents = pp.out.getvalue()
39
40    # Load into an OrderedDict to ensure order is preserved. Note: Python 3.7
41    # also preserves ordering for normal dictionaries.
42    # Code based on https://stackoverflow.com/a/21912744.
43    class OrderedLoader(yaml.Loader):
44        pass
45
46    def construct_mapping(loader, node):
47        loader.flatten_mapping(node)
48        return OrderedDict(loader.construct_pairs(node))
49
50    tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
51    OrderedLoader.add_constructor(tag, construct_mapping)
52    return yaml.load(contents, OrderedLoader)
53
54
55def generate_header(c_out, includeguard, contents):
56    c_out.write(
57        HEADER_TEMPLATE
58        % {
59            "includeguard": includeguard,
60            "contents": contents,
61        }
62    )
63
64
65operand_types = {
66    "WordSized": "LAllocation",
67    "BoxedValue": "LBoxAllocation",
68    "Int64": "LInt64Allocation",
69}
70
71
72result_types = {
73    "WordSized": "1",
74    "BoxedValue": "BOX_PIECES",
75    "Int64": "INT64_PIECES",
76}
77
78
79def gen_helper_template_value(num_regular_allocs, num_value_allocs, num_int64_allocs):
80    template_str = ""
81    if num_value_allocs:
82        template_str += str(num_value_allocs) + " * BOX_PIECES + "
83    if num_int64_allocs:
84        template_str += str(num_int64_allocs) + " * INT64_PIECES + "
85    template_str += str(num_regular_allocs)
86    return template_str
87
88
89def build_index_def(num_specials_operands, index_value, num_reg_operands, piece):
90    if num_specials_operands:
91        return "  static const size_t {} = {} + {} * {};\\\n".format(
92            index_value, num_reg_operands, piece, num_specials_operands
93        )
94    else:
95        return "  static const size_t {} = {};\\\n".format(
96            index_value, num_reg_operands
97        )
98
99
100def gen_lir_class(
101    name, result_type, operands, arguments, num_temps, call_instruction, mir_op
102):
103    """Generates class definition for a single LIR opcode."""
104    class_name = "L" + name
105
106    getters = []
107    setters = []
108    # Operand index definitions.
109    oper_indices = []
110    # Parameters for the class constructor.
111    constructor_params = []
112
113    num_reg_operands = 0
114    num_value_operands = 0
115    num_int64_operands = 0
116    if operands:
117        # Get number of LAllocations to use for defining indices.
118        for operand in operands:
119            if operands[operand] == "WordSized":
120                num_reg_operands += 1
121
122        current_reg_oper = 0
123        for operand in operands:
124            op_type = operands[operand]
125            op_alloc_type = operand_types[op_type]
126            constructor_params.append("const " + op_alloc_type + "& " + operand)
127            if op_type == "WordSized":
128                index_value = str(current_reg_oper)
129                current_reg_oper += 1
130                getters.append(
131                    "  const "
132                    + op_alloc_type
133                    + "* "
134                    + operand
135                    + "() { return getOperand("
136                    + index_value
137                    + "); }"
138                )
139                setters.append("    setOperand(" + index_value + ", " + operand + ");")
140            elif op_type == "BoxedValue":
141                index_value = operand[0].upper() + operand[1:] + "Index"
142                oper_indices.append(
143                    build_index_def(
144                        num_value_operands, index_value, num_reg_operands, "BOX_PIECES"
145                    )
146                )
147                num_value_operands += 1
148                # No getters generated for BoxedValue operands.
149                setters.append(
150                    "    setBoxOperand(" + index_value + ", " + operand + ");"
151                )
152            elif op_type == "Int64":
153                index_value = operand[0].upper() + operand[1:] + "Index"
154                oper_indices.append(
155                    build_index_def(
156                        num_int64_operands,
157                        index_value,
158                        num_reg_operands,
159                        "INT64_PIECES",
160                    )
161                )
162                num_int64_operands += 1
163                getters.append(
164                    "  const "
165                    + op_alloc_type
166                    + " "
167                    + operand
168                    + "() { return getInt64Operand("
169                    + index_value
170                    + "); }"
171                )
172                setters.append(
173                    "    setInt64Operand(" + index_value + ", " + operand + ");"
174                )
175            else:
176                raise Exception("Invalid operand type: " + op_type)
177    if num_temps:
178        for temp in range(num_temps):
179            constructor_params.append("const LDefinition& temp" + str(temp))
180            setters.append("    setTemp(" + str(temp) + ", temp" + str(temp) + ");")
181            getters.append(
182                "  const LDefinition* temp"
183                + str(temp)
184                + "() { return getTemp("
185                + str(temp)
186                + "); }"
187            )
188    code = "class {} : public LInstructionHelper<".format(class_name)
189    if result_type:
190        code += result_types[result_type] + ", "
191    else:
192        code += "0, "
193    code += gen_helper_template_value(
194        num_reg_operands, num_value_operands, num_int64_operands
195    )
196    code += ", {}> {{\\\n".format(num_temps)
197    if arguments:
198        for arg_name in arguments:
199            arg_type_sig = arguments[arg_name]
200            constructor_params.append(arg_type_sig + " " + arg_name)
201            code += "  " + arg_type_sig + " " + arg_name + "_;\\\n"
202    code += " public:\\\n  LIR_HEADER({})\\\n".format(name)
203    code += "  explicit {}(".format(class_name)
204    code += ", ".join(constructor_params)
205    code += ") : LInstructionHelper(classOpcode)"
206    if arguments:
207        for arg_name in arguments:
208            code += ", " + arg_name + "_(" + arg_name + ")"
209    code += " {"
210    if call_instruction:
211        code += "\\\n    this->setIsCall();"
212    code += "\\\n"
213    code += "\\\n".join(setters)
214    code += "\\\n  }\\\n"
215    code += "\\\n".join(getters)
216    if arguments:
217        for arg_name in arguments:
218            code += "  " + arguments[arg_name] + " " + arg_name + "() const { "
219            code += "return " + arg_name + "_; }\\\n"
220    code += "\\\n"
221    if operands:
222        code += "\\\n".join(oper_indices)
223    if mir_op:
224        if mir_op is True:
225            code += "  M{}* mir() const {{ return mir_->to{}(); }};\\\n".format(
226                name, name
227            )
228        else:
229            code += "  M{}* mir() const {{ return mir_->to{}(); }};\\\n".format(
230                mir_op, mir_op
231            )
232    code += "};\\\n"
233    return code
234
235
236def generate_lir_header(c_out, yaml_path):
237    data = load_yaml(yaml_path)
238
239    # LIR_OPCODE_LIST opcode.
240    ops = []
241
242    # Generated LIR op class definitions.
243    lir_op_classes = []
244
245    for op in data:
246        name = op["name"]
247
248        gen_boilerplate = op.get("gen_boilerplate", True)
249        assert isinstance(gen_boilerplate, bool)
250
251        if gen_boilerplate:
252            result_type = op.get("result_type", None)
253            assert result_type is None or str
254            if result_type:
255                assert result_types[result_type]
256
257            operands = op.get("operands", None)
258            assert operands is None or OrderedDict
259
260            arguments = op.get("arguments", None)
261            assert arguments is None or isinstance(arguments, OrderedDict)
262
263            num_temps = op.get("num_temps", 0)
264            assert num_temps is None or int
265
266            gen_boilerplate = op.get("gen_boilerplate", True)
267            assert isinstance(gen_boilerplate, bool)
268
269            call_instruction = op.get("call_instruction", None)
270            assert call_instruction is None or True
271
272            mir_op = op.get("mir_op", None)
273            assert mir_op is None or True or str
274
275            lir_op_classes.append(
276                gen_lir_class(
277                    name,
278                    result_type,
279                    operands,
280                    arguments,
281                    num_temps,
282                    call_instruction,
283                    mir_op,
284                )
285            )
286
287        ops.append("_({})".format(name))
288
289    contents = "#define LIR_OPCODE_LIST(_)\\\n"
290    contents += "\\\n".join(ops)
291    contents += "\n\n"
292
293    contents += "#define LIR_OPCODE_CLASS_GENERATED \\\n"
294    contents += "\\\n".join(lir_op_classes)
295    contents += "\n\n"
296
297    generate_header(c_out, "jit_LIROpsGenerated_h", contents)
298