Bug 1837620 - Part 1: Remove baseline ICs that guard shapes when the shape becomes...
[gecko.git] / js / src / jit / GenerateCacheIRFiles.py
blobf954f597343d6e040fce84df13fdf64fa7b0b716
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/.
5 # This script generates jit/CacheIROpsGenerated.h from CacheIROps.yaml
7 from collections import OrderedDict
9 import buildconfig
10 import six
11 import yaml
12 from mozbuild.preprocessor import Preprocessor
14 HEADER_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/. */
19 #ifndef %(includeguard)s
20 #define %(includeguard)s
22 /* This file is generated by jit/GenerateCacheIRFiles.py. Do not edit! */
24 %(contents)s
26 #endif // %(includeguard)s
27 """
30 def generate_header(c_out, includeguard, contents):
31 c_out.write(
32 HEADER_TEMPLATE
33 % {
34 "includeguard": includeguard,
35 "contents": contents,
40 def load_yaml(yaml_path):
41 # First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in
42 # the YAML file.
43 pp = Preprocessor()
44 pp.context.update(buildconfig.defines["ALLDEFINES"])
45 pp.out = six.StringIO()
46 pp.do_filter("substitution")
47 pp.do_include(yaml_path)
48 contents = pp.out.getvalue()
50 # Load into an OrderedDict to ensure order is preserved. Note: Python 3.7+
51 # also preserves ordering for normal dictionaries.
52 # Code based on https://stackoverflow.com/a/21912744.
53 class OrderedLoader(yaml.Loader):
54 pass
56 def construct_mapping(loader, node):
57 loader.flatten_mapping(node)
58 return OrderedDict(loader.construct_pairs(node))
60 tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
61 OrderedLoader.add_constructor(tag, construct_mapping)
62 return yaml.load(contents, OrderedLoader)
65 # Information for generating CacheIRWriter code for a single argument. Tuple
66 # stores the C++ argument type and the CacheIRWriter method to call.
67 arg_writer_info = {
68 "ValId": ("ValOperandId", "writeOperandId"),
69 "ObjId": ("ObjOperandId", "writeOperandId"),
70 "StringId": ("StringOperandId", "writeOperandId"),
71 "SymbolId": ("SymbolOperandId", "writeOperandId"),
72 "BooleanId": ("BooleanOperandId", "writeOperandId"),
73 "Int32Id": ("Int32OperandId", "writeOperandId"),
74 "NumberId": ("NumberOperandId", "writeOperandId"),
75 "BigIntId": ("BigIntOperandId", "writeOperandId"),
76 "ValueTagId": ("ValueTagOperandId", "writeOperandId"),
77 "IntPtrId": ("IntPtrOperandId", "writeOperandId"),
78 "RawId": ("OperandId", "writeOperandId"),
79 "ShapeField": ("Shape*", "writeShapeField"),
80 "WeakShapeField": ("Shape*", "writeWeakShapeField"),
81 "GetterSetterField": ("GetterSetter*", "writeGetterSetterField"),
82 "ObjectField": ("JSObject*", "writeObjectField"),
83 "StringField": ("JSString*", "writeStringField"),
84 "AtomField": ("JSAtom*", "writeStringField"),
85 "SymbolField": ("JS::Symbol*", "writeSymbolField"),
86 "BaseScriptField": ("BaseScript*", "writeBaseScriptField"),
87 "JitCodeField": ("JitCode*", "writeJitCodeField"),
88 "RawInt32Field": ("uint32_t", "writeRawInt32Field"),
89 "RawPointerField": ("const void*", "writeRawPointerField"),
90 "IdField": ("jsid", "writeIdField"),
91 "ValueField": ("const Value&", "writeValueField"),
92 "RawInt64Field": ("uint64_t", "writeRawInt64Field"),
93 "DoubleField": ("double", "writeDoubleField"),
94 "AllocSiteField": ("gc::AllocSite*", "writeAllocSiteField"),
95 "JSOpImm": ("JSOp", "writeJSOpImm"),
96 "BoolImm": ("bool", "writeBoolImm"),
97 "ByteImm": ("uint32_t", "writeByteImm"), # uint32_t to enable fits-in-byte asserts.
98 "GuardClassKindImm": ("GuardClassKind", "writeGuardClassKindImm"),
99 "ValueTypeImm": ("ValueType", "writeValueTypeImm"),
100 "JSWhyMagicImm": ("JSWhyMagic", "writeJSWhyMagicImm"),
101 "CallFlagsImm": ("CallFlags", "writeCallFlagsImm"),
102 "ScalarTypeImm": ("Scalar::Type", "writeScalarTypeImm"),
103 "UnaryMathFunctionImm": ("UnaryMathFunction", "writeUnaryMathFunctionImm"),
104 "WasmValTypeImm": ("wasm::ValType::Kind", "writeWasmValTypeImm"),
105 "Int32Imm": ("int32_t", "writeInt32Imm"),
106 "UInt32Imm": ("uint32_t", "writeUInt32Imm"),
107 "JSNativeImm": ("JSNative", "writeJSNativeImm"),
108 "StaticStringImm": ("const char*", "writeStaticStringImm"),
109 "AllocKindImm": ("gc::AllocKind", "writeAllocKindImm"),
110 "CompletionKindImm": ("CompletionKind", "writeCompletionKindImm"),
114 def gen_writer_method(name, args, custom_writer):
115 """Generates a CacheIRWRiter method for a single opcode."""
117 # Generate a single method that writes the opcode and each argument.
118 # For example:
120 # void guardShape(ObjOperandId obj, Shape* shape) {
121 # writeOp(CacheOp::GuardShape);
122 # writeOperandId(obj);
123 # writeShapeField(shape);
124 # assertLengthMatches();
127 # The assertLengthMatches() call is to assert the information in the
128 # arg_length dictionary below matches what's written.
130 # Method names start with a lowercase letter.
131 method_name = name[0].lower() + name[1:]
132 if custom_writer:
133 method_name += "_"
135 method_args = []
136 ret_type = "void"
137 args_code = ""
138 if args:
139 for arg_name, arg_type in six.iteritems(args):
140 cpp_type, write_method = arg_writer_info[arg_type]
141 if arg_name == "result":
142 ret_type = cpp_type
143 args_code += " {} result(newOperandId());\\\n".format(cpp_type)
144 args_code += " writeOperandId(result);\\\n"
145 else:
146 method_args.append("{} {}".format(cpp_type, arg_name))
147 args_code += " {}({});\\\n".format(write_method, arg_name)
149 code = ""
150 if custom_writer:
151 code += "private:\\\n"
152 code += "{} {}({}) {{\\\n".format(ret_type, method_name, ", ".join(method_args))
153 code += " writeOp(CacheOp::{});\\\n".format(name)
154 code += args_code
155 code += " assertLengthMatches();\\\n"
156 if ret_type != "void":
157 code += " return result;\\\n"
158 code += "}"
159 if custom_writer:
160 code += "\\\npublic:"
161 return code
164 # Information for generating code using CacheIRReader for a single argument.
165 # Tuple stores the C++ type, the suffix used for arguments/variables of this
166 # type, and the expression to read this type from CacheIRReader.
167 arg_reader_info = {
168 "ValId": ("ValOperandId", "Id", "reader.valOperandId()"),
169 "ObjId": ("ObjOperandId", "Id", "reader.objOperandId()"),
170 "StringId": ("StringOperandId", "Id", "reader.stringOperandId()"),
171 "SymbolId": ("SymbolOperandId", "Id", "reader.symbolOperandId()"),
172 "BooleanId": ("BooleanOperandId", "Id", "reader.booleanOperandId()"),
173 "Int32Id": ("Int32OperandId", "Id", "reader.int32OperandId()"),
174 "NumberId": ("NumberOperandId", "Id", "reader.numberOperandId()"),
175 "BigIntId": ("BigIntOperandId", "Id", "reader.bigIntOperandId()"),
176 "ValueTagId": ("ValueTagOperandId", "Id", "reader.valueTagOperandId()"),
177 "IntPtrId": ("IntPtrOperandId", "Id", "reader.intPtrOperandId()"),
178 "RawId": ("uint32_t", "Id", "reader.rawOperandId()"),
179 "ShapeField": ("uint32_t", "Offset", "reader.stubOffset()"),
180 "WeakShapeField": ("uint32_t", "Offset", "reader.stubOffset()"),
181 "GetterSetterField": ("uint32_t", "Offset", "reader.stubOffset()"),
182 "ObjectField": ("uint32_t", "Offset", "reader.stubOffset()"),
183 "StringField": ("uint32_t", "Offset", "reader.stubOffset()"),
184 "AtomField": ("uint32_t", "Offset", "reader.stubOffset()"),
185 "SymbolField": ("uint32_t", "Offset", "reader.stubOffset()"),
186 "BaseScriptField": ("uint32_t", "Offset", "reader.stubOffset()"),
187 "JitCodeField": ("uint32_t", "Offset", "reader.stubOffset()"),
188 "RawInt32Field": ("uint32_t", "Offset", "reader.stubOffset()"),
189 "RawPointerField": ("uint32_t", "Offset", "reader.stubOffset()"),
190 "IdField": ("uint32_t", "Offset", "reader.stubOffset()"),
191 "ValueField": ("uint32_t", "Offset", "reader.stubOffset()"),
192 "RawInt64Field": ("uint32_t", "Offset", "reader.stubOffset()"),
193 "DoubleField": ("uint32_t", "Offset", "reader.stubOffset()"),
194 "AllocSiteField": ("uint32_t", "Offset", "reader.stubOffset()"),
195 "JSOpImm": ("JSOp", "", "reader.jsop()"),
196 "BoolImm": ("bool", "", "reader.readBool()"),
197 "ByteImm": ("uint8_t", "", "reader.readByte()"),
198 "GuardClassKindImm": ("GuardClassKind", "", "reader.guardClassKind()"),
199 "ValueTypeImm": ("ValueType", "", "reader.valueType()"),
200 "JSWhyMagicImm": ("JSWhyMagic", "", "reader.whyMagic()"),
201 "CallFlagsImm": ("CallFlags", "", "reader.callFlags()"),
202 "ScalarTypeImm": ("Scalar::Type", "", "reader.scalarType()"),
203 "UnaryMathFunctionImm": ("UnaryMathFunction", "", "reader.unaryMathFunction()"),
204 "WasmValTypeImm": ("wasm::ValType::Kind", "", "reader.wasmValType()"),
205 "Int32Imm": ("int32_t", "", "reader.int32Immediate()"),
206 "UInt32Imm": ("uint32_t", "", "reader.uint32Immediate()"),
207 "JSNativeImm": ("JSNative", "", "reinterpret_cast<JSNative>(reader.pointer())"),
208 "StaticStringImm": ("const char*", "", "reinterpret_cast<char*>(reader.pointer())"),
209 "AllocKindImm": ("gc::AllocKind", "", "reader.allocKind()"),
210 "CompletionKindImm": ("CompletionKind", "", "reader.completionKind()"),
214 def gen_compiler_method(name, args):
215 """Generates CacheIRCompiler or WarpCacheIRTranspiler header code for a
216 single opcode."""
218 method_name = "emit" + name
220 # We generate the signature of the method that needs to be implemented and a
221 # separate function forwarding to it. For example:
223 # [[nodiscard]] bool emitGuardShape(ObjOperandId objId, uint32_t shapeOffset);
224 # [[nodiscard]] bool emitGuardShape(CacheIRReader& reader) {
225 # ObjOperandId objId = reader.objOperandId();
226 # uint32_t shapeOffset = reader.stubOffset();
227 # return emitGuardShape(objId, shapeOffset);
229 cpp_args = []
230 method_args = []
231 args_code = ""
232 if args:
233 for arg_name, arg_type in six.iteritems(args):
234 cpp_type, suffix, readexpr = arg_reader_info[arg_type]
235 cpp_name = arg_name + suffix
236 cpp_args.append(cpp_name)
237 method_args.append("{} {}".format(cpp_type, cpp_name))
238 args_code += " {} {} = {};\\\n".format(cpp_type, cpp_name, readexpr)
240 # Generate signature.
241 code = "[[nodiscard]] bool {}({});\\\n".format(method_name, ", ".join(method_args))
243 # Generate the method forwarding to it.
244 code += "[[nodiscard]] bool {}(CacheIRReader& reader) {{\\\n".format(method_name)
245 code += args_code
246 code += " return {}({});\\\n".format(method_name, ", ".join(cpp_args))
247 code += "}\\\n"
249 return code
252 # For each argument type, the method name for printing it.
253 arg_spewer_method = {
254 "ValId": "spewOperandId",
255 "ObjId": "spewOperandId",
256 "StringId": "spewOperandId",
257 "SymbolId": "spewOperandId",
258 "BooleanId": "spewOperandId",
259 "Int32Id": "spewOperandId",
260 "NumberId": "spewOperandId",
261 "BigIntId": "spewOperandId",
262 "ValueTagId": "spewOperandId",
263 "IntPtrId": "spewOperandId",
264 "RawId": "spewRawOperandId",
265 "ShapeField": "spewField",
266 "WeakShapeField": "spewField",
267 "GetterSetterField": "spewField",
268 "ObjectField": "spewField",
269 "StringField": "spewField",
270 "AtomField": "spewField",
271 "SymbolField": "spewField",
272 "BaseScriptField": "spewField",
273 "JitCodeField": "spewField",
274 "RawInt32Field": "spewField",
275 "RawPointerField": "spewField",
276 "IdField": "spewField",
277 "ValueField": "spewField",
278 "RawInt64Field": "spewField",
279 "DoubleField": "spewField",
280 "AllocSiteField": "spewField",
281 "JSOpImm": "spewJSOpImm",
282 "BoolImm": "spewBoolImm",
283 "ByteImm": "spewByteImm",
284 "GuardClassKindImm": "spewGuardClassKindImm",
285 "ValueTypeImm": "spewValueTypeImm",
286 "JSWhyMagicImm": "spewJSWhyMagicImm",
287 "CallFlagsImm": "spewCallFlagsImm",
288 "ScalarTypeImm": "spewScalarTypeImm",
289 "UnaryMathFunctionImm": "spewUnaryMathFunctionImm",
290 "WasmValTypeImm": "spewWasmValTypeImm",
291 "Int32Imm": "spewInt32Imm",
292 "UInt32Imm": "spewUInt32Imm",
293 "JSNativeImm": "spewJSNativeImm",
294 "StaticStringImm": "spewStaticStringImm",
295 "AllocKindImm": "spewAllocKindImm",
296 "CompletionKindImm": "spewCompletionKindImm",
300 def gen_spewer_method(name, args):
301 """Generates spewer code for a single opcode."""
303 method_name = "spew" + name
305 # Generate code like this:
307 # void spewGuardShape(CacheIRReader& reader) {
308 # spewOp(CacheOp::GuardShape);
309 # spewOperandId("objId", reader.objOperandId());
310 # spewOperandSeparator();
311 # spewField("shapeOffset", reader.stubOffset());
312 # spewOpEnd();
314 args_code = ""
315 if args:
316 is_first = True
317 for arg_name, arg_type in six.iteritems(args):
318 _, suffix, readexpr = arg_reader_info[arg_type]
319 arg_name += suffix
320 spew_method = arg_spewer_method[arg_type]
321 if not is_first:
322 args_code += " spewArgSeparator();\\\n"
323 args_code += ' {}("{}", {});\\\n'.format(spew_method, arg_name, readexpr)
324 is_first = False
326 code = "void {}(CacheIRReader& reader) {{\\\n".format(method_name)
327 code += " spewOp(CacheOp::{});\\\n".format(name)
328 code += args_code
329 code += " spewOpEnd();\\\n"
330 code += "}\\\n"
332 return code
335 def gen_clone_method(name, args):
336 """Generates code for cloning a single opcode."""
338 method_name = "clone" + name
340 # Generate code like this:
342 # void cloneGuardShape(CacheIRReader& reader, CacheIRWriter& writer) {
343 # writer.writeOp(CacheOp::GuardShape);
344 # ObjOperandId objId = reader.objOperandId();
345 # writer.writeOperandId(objId);
346 # uint32_t shapeOffset = reader.stubOffset();
347 # Shape* shape = getShapeField(shapeOffset);
348 # writer.writeShapeField(shape);
349 # writer.assertLengthMatches();
352 args_code = ""
353 if args:
354 for arg_name, arg_type in six.iteritems(args):
355 if arg_type == "RawId":
356 arg_type = "ValId"
358 read_type, suffix, readexpr = arg_reader_info[arg_type]
359 read_name = arg_name + suffix
360 value_name = read_name
361 args_code += " {} {} = {};\\\n".format(read_type, read_name, readexpr)
363 write_type, write_method = arg_writer_info[arg_type]
364 if arg_name == "result":
365 args_code += " writer.newOperandId();\\\n"
366 if suffix == "Offset":
367 # If the write function takes T&, the intermediate variable
368 # should be of type T.
369 if write_type.endswith("&"):
370 write_type = write_type[:-1]
371 value_name = arg_name
372 args_code += " {} {} = get{}({});\\\n".format(
373 write_type, value_name, arg_type, read_name
375 args_code += " writer.{}({});\\\n".format(write_method, value_name)
377 code = "void {}".format(method_name)
378 code += "(CacheIRReader& reader, CacheIRWriter& writer) {{\\\n"
379 code += " writer.writeOp(CacheOp::{});\\\n".format(name)
380 code += args_code
381 code += " writer.assertLengthMatches();\\\n"
382 code += "}}\\\n"
384 return code
387 # Length in bytes for each argument type, either an integer or a C++ expression.
388 # This is used to generate the CacheIROpArgLengths array. CacheIRWriter asserts
389 # the number of bytes written matches the value in that array.
390 arg_length = {
391 "ValId": 1,
392 "ObjId": 1,
393 "StringId": 1,
394 "SymbolId": 1,
395 "BooleanId": 1,
396 "Int32Id": 1,
397 "NumberId": 1,
398 "BigIntId": 1,
399 "ValueTagId": 1,
400 "IntPtrId": 1,
401 "RawId": 1,
402 "ShapeField": 1,
403 "WeakShapeField": 1,
404 "GetterSetterField": 1,
405 "ObjectField": 1,
406 "StringField": 1,
407 "AtomField": 1,
408 "SymbolField": 1,
409 "BaseScriptField": 1,
410 "JitCodeField": 1,
411 "RawInt32Field": 1,
412 "RawPointerField": 1,
413 "RawInt64Field": 1,
414 "DoubleField": 1,
415 "IdField": 1,
416 "ValueField": 1,
417 "AllocSiteField": 1,
418 "ByteImm": 1,
419 "BoolImm": 1,
420 "CallFlagsImm": 1,
421 "ScalarTypeImm": 1,
422 "UnaryMathFunctionImm": 1,
423 "JSOpImm": 1,
424 "ValueTypeImm": 1,
425 "GuardClassKindImm": 1,
426 "JSWhyMagicImm": 1,
427 "WasmValTypeImm": 1,
428 "Int32Imm": 4,
429 "UInt32Imm": 4,
430 "JSNativeImm": "sizeof(uintptr_t)",
431 "StaticStringImm": "sizeof(uintptr_t)",
432 "AllocKindImm": 1,
433 "CompletionKindImm": 1,
437 def generate_cacheirops_header(c_out, yaml_path):
438 """Generate CacheIROpsGenerated.h from CacheIROps.yaml. The generated file
439 contains a list of all CacheIR ops and generated source code for
440 CacheIRWriter and CacheIRCompiler."""
442 data = load_yaml(yaml_path)
444 # CACHE_IR_OPS items. Each item stores an opcode name and arguments length
445 # expression. For example: _(GuardShape, 1 + 1)
446 ops_items = []
448 # Generated CacheIRWriter methods.
449 writer_methods = []
451 # Generated CacheIRCompiler methods.
452 compiler_shared_methods = []
453 compiler_unshared_methods = []
455 # Generated WarpCacheIRTranspiler methods.
456 transpiler_methods = []
458 # List of ops supported by WarpCacheIRTranspiler.
459 transpiler_ops = []
461 # Generated methods for spewers.
462 spewer_methods = []
464 # Generated methods for cloning IC stubs
465 clone_methods = []
467 for op in data:
468 name = op["name"]
470 args = op["args"]
471 assert args is None or isinstance(args, OrderedDict)
473 shared = op["shared"]
474 assert isinstance(shared, bool)
476 transpile = op["transpile"]
477 assert isinstance(transpile, bool)
479 # Unscored Ops default to UINT32_MAX
480 cost_estimate = op.get("cost_estimate", int(0xFFFFFFFF))
481 assert isinstance(cost_estimate, int)
483 custom_writer = op.get("custom_writer", False)
484 assert isinstance(custom_writer, bool)
486 if args:
487 args_length = " + ".join([str(arg_length[v]) for v in args.values()])
488 else:
489 args_length = "0"
491 transpile_str = "true" if transpile else "false"
492 ops_items.append(
493 "_({}, {}, {}, {})".format(name, args_length, transpile_str, cost_estimate)
496 writer_methods.append(gen_writer_method(name, args, custom_writer))
498 if shared:
499 compiler_shared_methods.append(gen_compiler_method(name, args))
500 else:
501 compiler_unshared_methods.append(gen_compiler_method(name, args))
503 if transpile:
504 transpiler_methods.append(gen_compiler_method(name, args))
505 transpiler_ops.append("_({})".format(name))
507 spewer_methods.append(gen_spewer_method(name, args))
509 clone_methods.append(gen_clone_method(name, args))
511 contents = "#define CACHE_IR_OPS(_)\\\n"
512 contents += "\\\n".join(ops_items)
513 contents += "\n\n"
515 contents += "#define CACHE_IR_WRITER_GENERATED \\\n"
516 contents += "\\\n".join(writer_methods)
517 contents += "\n\n"
519 contents += "#define CACHE_IR_COMPILER_SHARED_GENERATED \\\n"
520 contents += "\\\n".join(compiler_shared_methods)
521 contents += "\n\n"
523 contents += "#define CACHE_IR_COMPILER_UNSHARED_GENERATED \\\n"
524 contents += "\\\n".join(compiler_unshared_methods)
525 contents += "\n\n"
527 contents += "#define CACHE_IR_TRANSPILER_GENERATED \\\n"
528 contents += "\\\n".join(transpiler_methods)
529 contents += "\n\n"
531 contents += "#define CACHE_IR_TRANSPILER_OPS(_)\\\n"
532 contents += "\\\n".join(transpiler_ops)
533 contents += "\n\n"
535 contents += "#define CACHE_IR_SPEWER_GENERATED \\\n"
536 contents += "\\\n".join(spewer_methods)
537 contents += "\n\n"
539 contents += "#define CACHE_IR_CLONE_GENERATED \\\n"
540 contents += "\\\n".join(clone_methods)
541 contents += "\n\n"
543 generate_header(c_out, "jit_CacheIROpsGenerated_h", contents)