diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit/GenerateCacheIRFiles.py | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/GenerateCacheIRFiles.py')
-rw-r--r-- | js/src/jit/GenerateCacheIRFiles.py | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/js/src/jit/GenerateCacheIRFiles.py b/js/src/jit/GenerateCacheIRFiles.py new file mode 100644 index 0000000000..08f0eda0fd --- /dev/null +++ b/js/src/jit/GenerateCacheIRFiles.py @@ -0,0 +1,518 @@ +# 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/CacheIROpsGenerated.h from CacheIROps.yaml + +import buildconfig +import yaml +import six +from collections import OrderedDict +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/GenerateCacheIRFiles.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() + + # Load into an OrderedDict to ensure order is preserved. Note: Python 3.7+ + # also preserves ordering for normal dictionaries. + # Code based on https://stackoverflow.com/a/21912744. + class OrderedLoader(yaml.Loader): + pass + + def construct_mapping(loader, node): + loader.flatten_mapping(node) + return OrderedDict(loader.construct_pairs(node)) + + tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG + OrderedLoader.add_constructor(tag, construct_mapping) + return yaml.load(contents, OrderedLoader) + + +# Information for generating CacheIRWriter code for a single argument. Tuple +# stores the C++ argument type and the CacheIRWriter method to call. +arg_writer_info = { + "ValId": ("ValOperandId", "writeOperandId"), + "ObjId": ("ObjOperandId", "writeOperandId"), + "StringId": ("StringOperandId", "writeOperandId"), + "SymbolId": ("SymbolOperandId", "writeOperandId"), + "BooleanId": ("BooleanOperandId", "writeOperandId"), + "Int32Id": ("Int32OperandId", "writeOperandId"), + "NumberId": ("NumberOperandId", "writeOperandId"), + "BigIntId": ("BigIntOperandId", "writeOperandId"), + "ValueTagId": ("ValueTagOperandId", "writeOperandId"), + "RawId": ("OperandId", "writeOperandId"), + "ShapeField": ("Shape*", "writeShapeField"), + "GroupField": ("ObjectGroup*", "writeGroupField"), + "ObjectField": ("JSObject*", "writeObjectField"), + "StringField": ("JSString*", "writeStringField"), + "AtomField": ("JSAtom*", "writeStringField"), + "PropertyNameField": ("PropertyName*", "writeStringField"), + "SymbolField": ("JS::Symbol*", "writeSymbolField"), + "BaseScriptField": ("BaseScript*", "writeBaseScriptField"), + "RawInt32Field": ("uint32_t", "writeRawInt32Field"), + "RawPointerField": ("const void*", "writeRawPointerField"), + "IdField": ("jsid", "writeIdField"), + "ValueField": ("const Value&", "writeValueField"), + "RawInt64Field": ("uint64_t", "writeRawInt64Field"), + "JSOpImm": ("JSOp", "writeJSOpImm"), + "BoolImm": ("bool", "writeBoolImm"), + "ByteImm": ("uint32_t", "writeByteImm"), # uint32_t to enable fits-in-byte asserts. + "GuardClassKindImm": ("GuardClassKind", "writeGuardClassKindImm"), + "ValueTypeImm": ("ValueType", "writeValueTypeImm"), + "JSWhyMagicImm": ("JSWhyMagic", "writeJSWhyMagicImm"), + "CallFlagsImm": ("CallFlags", "writeCallFlagsImm"), + "ScalarTypeImm": ("Scalar::Type", "writeScalarTypeImm"), + "UnaryMathFunctionImm": ("UnaryMathFunction", "writeUnaryMathFunctionImm"), + "WasmValTypeImm": ("wasm::ValType::Kind", "writeWasmValTypeImm"), + "Int32Imm": ("int32_t", "writeInt32Imm"), + "UInt32Imm": ("uint32_t", "writeUInt32Imm"), + "JSNativeImm": ("JSNative", "writeJSNativeImm"), + "StaticStringImm": ("const char*", "writeStaticStringImm"), +} + + +def gen_writer_method(name, args, custom_writer): + """Generates a CacheIRWRiter method for a single opcode.""" + + # Generate a single method that writes the opcode and each argument. + # For example: + # + # void guardShape(ObjOperandId obj, Shape* shape) { + # writeOp(CacheOp::GuardShape); + # writeOperandId(obj); + # writeShapeField(shape); + # assertLengthMatches(); + # } + # + # The assertLengthMatches() call is to assert the information in the + # arg_length dictionary below matches what's written. + + # Method names start with a lowercase letter. + method_name = name[0].lower() + name[1:] + if custom_writer: + method_name += "_" + + method_args = [] + ret_type = "void" + args_code = "" + if args: + for arg_name, arg_type in six.iteritems(args): + cpp_type, write_method = arg_writer_info[arg_type] + if arg_name == "result": + ret_type = cpp_type + args_code += " {} result(newOperandId());\\\n".format(cpp_type) + args_code += " writeOperandId(result);\\\n" + else: + method_args.append("{} {}".format(cpp_type, arg_name)) + args_code += " {}({});\\\n".format(write_method, arg_name) + + code = "" + if custom_writer: + code += "private:\\\n" + code += "{} {}({}) {{\\\n".format(ret_type, method_name, ", ".join(method_args)) + code += " writeOp(CacheOp::{});\\\n".format(name) + code += args_code + code += " assertLengthMatches();\\\n" + if ret_type != "void": + code += " return result;\\\n" + code += "}" + if custom_writer: + code += "\\\npublic:" + return code + + +# Information for generating code using CacheIRReader for a single argument. +# Tuple stores the C++ type, the suffix used for arguments/variables of this +# type, and the expression to read this type from CacheIRReader. +arg_reader_info = { + "ValId": ("ValOperandId", "Id", "reader.valOperandId()"), + "ObjId": ("ObjOperandId", "Id", "reader.objOperandId()"), + "StringId": ("StringOperandId", "Id", "reader.stringOperandId()"), + "SymbolId": ("SymbolOperandId", "Id", "reader.symbolOperandId()"), + "BooleanId": ("BooleanOperandId", "Id", "reader.booleanOperandId()"), + "Int32Id": ("Int32OperandId", "Id", "reader.int32OperandId()"), + "NumberId": ("NumberOperandId", "Id", "reader.numberOperandId()"), + "BigIntId": ("BigIntOperandId", "Id", "reader.bigIntOperandId()"), + "ValueTagId": ("ValueTagOperandId", "Id", "reader.valueTagOperandId()"), + "RawId": ("uint32_t", "Id", "reader.rawOperandId()"), + "ShapeField": ("uint32_t", "Offset", "reader.stubOffset()"), + "GroupField": ("uint32_t", "Offset", "reader.stubOffset()"), + "ObjectField": ("uint32_t", "Offset", "reader.stubOffset()"), + "StringField": ("uint32_t", "Offset", "reader.stubOffset()"), + "AtomField": ("uint32_t", "Offset", "reader.stubOffset()"), + "PropertyNameField": ("uint32_t", "Offset", "reader.stubOffset()"), + "SymbolField": ("uint32_t", "Offset", "reader.stubOffset()"), + "BaseScriptField": ("uint32_t", "Offset", "reader.stubOffset()"), + "RawInt32Field": ("uint32_t", "Offset", "reader.stubOffset()"), + "RawPointerField": ("uint32_t", "Offset", "reader.stubOffset()"), + "IdField": ("uint32_t", "Offset", "reader.stubOffset()"), + "ValueField": ("uint32_t", "Offset", "reader.stubOffset()"), + "RawInt64Field": ("uint32_t", "Offset", "reader.stubOffset()"), + "JSOpImm": ("JSOp", "", "reader.jsop()"), + "BoolImm": ("bool", "", "reader.readBool()"), + "ByteImm": ("uint8_t", "", "reader.readByte()"), + "GuardClassKindImm": ("GuardClassKind", "", "reader.guardClassKind()"), + "ValueTypeImm": ("ValueType", "", "reader.valueType()"), + "JSWhyMagicImm": ("JSWhyMagic", "", "reader.whyMagic()"), + "CallFlagsImm": ("CallFlags", "", "reader.callFlags()"), + "ScalarTypeImm": ("Scalar::Type", "", "reader.scalarType()"), + "UnaryMathFunctionImm": ("UnaryMathFunction", "", "reader.unaryMathFunction()"), + "WasmValTypeImm": ("wasm::ValType::Kind", "", "reader.wasmValType()"), + "Int32Imm": ("int32_t", "", "reader.int32Immediate()"), + "UInt32Imm": ("uint32_t", "", "reader.uint32Immediate()"), + "JSNativeImm": ("JSNative", "", "reinterpret_cast<JSNative>(reader.pointer())"), + "StaticStringImm": ("const char*", "", "reinterpret_cast<char*>(reader.pointer())"), +} + + +def gen_compiler_method(name, args): + """Generates CacheIRCompiler or WarpCacheIRTranspiler header code for a + single opcode.""" + + method_name = "emit" + name + + # We generate the signature of the method that needs to be implemented and a + # separate function forwarding to it. For example: + # + # [[nodiscard]] bool emitGuardShape(ObjOperandId objId, uint32_t shapeOffset); + # [[nodiscard]] bool emitGuardShape(CacheIRReader& reader) { + # ObjOperandId objId = reader.objOperandId(); + # uint32_t shapeOffset = reader.stubOffset(); + # return emitGuardShape(objId, shapeOffset); + # } + cpp_args = [] + method_args = [] + args_code = "" + if args: + for arg_name, arg_type in six.iteritems(args): + cpp_type, suffix, readexpr = arg_reader_info[arg_type] + cpp_name = arg_name + suffix + cpp_args.append(cpp_name) + method_args.append("{} {}".format(cpp_type, cpp_name)) + args_code += " {} {} = {};\\\n".format(cpp_type, cpp_name, readexpr) + + # Generate signature. + code = "[[nodiscard]] bool {}({});\\\n".format(method_name, ", ".join(method_args)) + + # Generate the method forwarding to it. + code += "[[nodiscard]] bool {}(CacheIRReader& reader) {{\\\n".format(method_name) + code += args_code + code += " return {}({});\\\n".format(method_name, ", ".join(cpp_args)) + code += "}\\\n" + + return code + + +# For each argument type, the method name for printing it. +arg_spewer_method = { + "ValId": "spewOperandId", + "ObjId": "spewOperandId", + "StringId": "spewOperandId", + "SymbolId": "spewOperandId", + "BooleanId": "spewOperandId", + "Int32Id": "spewOperandId", + "NumberId": "spewOperandId", + "BigIntId": "spewOperandId", + "ValueTagId": "spewOperandId", + "RawId": "spewRawOperandId", + "ShapeField": "spewField", + "GroupField": "spewField", + "ObjectField": "spewField", + "StringField": "spewField", + "AtomField": "spewField", + "PropertyNameField": "spewField", + "SymbolField": "spewField", + "BaseScriptField": "spewField", + "RawInt32Field": "spewField", + "RawPointerField": "spewField", + "IdField": "spewField", + "ValueField": "spewField", + "RawInt64Field": "spewField", + "JSOpImm": "spewJSOpImm", + "BoolImm": "spewBoolImm", + "ByteImm": "spewByteImm", + "GuardClassKindImm": "spewGuardClassKindImm", + "ValueTypeImm": "spewValueTypeImm", + "JSWhyMagicImm": "spewJSWhyMagicImm", + "CallFlagsImm": "spewCallFlagsImm", + "ScalarTypeImm": "spewScalarTypeImm", + "UnaryMathFunctionImm": "spewUnaryMathFunctionImm", + "WasmValTypeImm": "spewWasmValTypeImm", + "Int32Imm": "spewInt32Imm", + "UInt32Imm": "spewUInt32Imm", + "JSNativeImm": "spewJSNativeImm", + "StaticStringImm": "spewStaticStringImm", +} + + +def gen_spewer_method(name, args): + """Generates spewer code for a single opcode.""" + + method_name = "spew" + name + + # Generate code like this: + # + # void spewGuardShape(CacheIRReader& reader) { + # spewOp(CacheOp::GuardShape); + # spewOperandId("objId", reader.objOperandId()); + # spewOperandSeparator(); + # spewField("shapeOffset", reader.stubOffset()); + # spewOpEnd(); + # } + args_code = "" + if args: + is_first = True + for arg_name, arg_type in six.iteritems(args): + _, suffix, readexpr = arg_reader_info[arg_type] + arg_name += suffix + spew_method = arg_spewer_method[arg_type] + if not is_first: + args_code += " spewArgSeparator();\\\n" + args_code += ' {}("{}", {});\\\n'.format(spew_method, arg_name, readexpr) + is_first = False + + code = "void {}(CacheIRReader& reader) {{\\\n".format(method_name) + code += " spewOp(CacheOp::{});\\\n".format(name) + code += args_code + code += " spewOpEnd();\\\n" + code += "}\\\n" + + return code + + +def gen_clone_method(name, args): + """Generates code for cloning a single opcode.""" + + method_name = "clone" + name + + # Generate code like this: + # + # void cloneGuardShape(CacheIRReader& reader, CacheIRWriter& writer) { + # writer.writeOp(CacheOp::GuardShape); + # ObjOperandId objId = reader.objOperandId(); + # writer.writeOperandId(objId); + # uint32_t shapeOffset = reader.stubOffset(); + # Shape* shape = getShapeField(shapeOffset); + # writer.writeShapeField(shape); + # writer.assertLengthMatches(); + # } + + args_code = "" + if args: + for arg_name, arg_type in six.iteritems(args): + if arg_type == "RawId": + arg_type = "ValId" + + read_type, suffix, readexpr = arg_reader_info[arg_type] + read_name = arg_name + suffix + value_name = read_name + args_code += " {} {} = {};\\\n".format(read_type, read_name, readexpr) + + write_type, write_method = arg_writer_info[arg_type] + if arg_name == "result": + args_code += " writer.newOperandId();\\\n" + if suffix == "Offset": + # If the write function takes T&, the intermediate variable + # should be of type T. + if write_type.endswith("&"): + write_type = write_type[:-1] + value_name = arg_name + args_code += " {} {} = get{}({});\\\n".format( + write_type, value_name, arg_type, read_name + ) + args_code += " writer.{}({});\\\n".format(write_method, value_name) + + code = "void {}".format(method_name) + code += "(CacheIRReader& reader, CacheIRWriter& writer) {{\\\n" + code += " writer.writeOp(CacheOp::{});\\\n".format(name) + code += args_code + code += " writer.assertLengthMatches();\\\n" + code += "}}\\\n" + + return code + + +# Length in bytes for each argument type, either an integer or a C++ expression. +# This is used to generate the CacheIROpArgLengths array. CacheIRWriter asserts +# the number of bytes written matches the value in that array. +arg_length = { + "ValId": 1, + "ObjId": 1, + "StringId": 1, + "SymbolId": 1, + "BooleanId": 1, + "Int32Id": 1, + "NumberId": 1, + "BigIntId": 1, + "ValueTagId": 1, + "RawId": 1, + "ShapeField": 1, + "GroupField": 1, + "ObjectField": 1, + "StringField": 1, + "AtomField": 1, + "PropertyNameField": 1, + "SymbolField": 1, + "BaseScriptField": 1, + "RawInt32Field": 1, + "RawPointerField": 1, + "RawInt64Field": 1, + "IdField": 1, + "ValueField": 1, + "ByteImm": 1, + "BoolImm": 1, + "CallFlagsImm": 1, + "ScalarTypeImm": 1, + "UnaryMathFunctionImm": 1, + "JSOpImm": 1, + "ValueTypeImm": 1, + "GuardClassKindImm": 1, + "JSWhyMagicImm": 1, + "WasmValTypeImm": 1, + "Int32Imm": 4, + "UInt32Imm": 4, + "JSNativeImm": "sizeof(uintptr_t)", + "StaticStringImm": "sizeof(uintptr_t)", +} + + +def generate_cacheirops_header(c_out, yaml_path): + """Generate CacheIROpsGenerated.h from CacheIROps.yaml. The generated file + contains a list of all CacheIR ops and generated source code for + CacheIRWriter and CacheIRCompiler.""" + + data = load_yaml(yaml_path) + + # CACHE_IR_OPS items. Each item stores an opcode name and arguments length + # expression. For example: _(GuardShape, 1 + 1) + ops_items = [] + + # Generated CacheIRWriter methods. + writer_methods = [] + + # Generated CacheIRCompiler methods. + compiler_shared_methods = [] + compiler_unshared_methods = [] + + # Generated WarpCacheIRTranspiler methods. + transpiler_methods = [] + + # List of ops supported by WarpCacheIRTranspiler. + transpiler_ops = [] + + # Generated methods for spewers. + spewer_methods = [] + + # Generated methods for cloning IC stubs + clone_methods = [] + + for op in data: + name = op["name"] + + args = op["args"] + assert args is None or isinstance(args, OrderedDict) + + shared = op["shared"] + assert isinstance(shared, bool) + + transpile = op["transpile"] + assert isinstance(transpile, bool) + + # Unscored Ops default to UINT32_MAX + cost_estimate = op.get("cost_estimate", int(0xFFFFFFFF)) + assert isinstance(cost_estimate, int) + + custom_writer = op.get("custom_writer", False) + assert isinstance(custom_writer, bool) + + if args: + args_length = " + ".join([str(arg_length[v]) for v in args.values()]) + else: + args_length = "0" + + transpile_str = "true" if transpile else "false" + ops_items.append( + "_({}, {}, {}, {})".format(name, args_length, transpile_str, cost_estimate) + ) + + writer_methods.append(gen_writer_method(name, args, custom_writer)) + + if shared: + compiler_shared_methods.append(gen_compiler_method(name, args)) + else: + compiler_unshared_methods.append(gen_compiler_method(name, args)) + + if transpile: + transpiler_methods.append(gen_compiler_method(name, args)) + transpiler_ops.append("_({})".format(name)) + + spewer_methods.append(gen_spewer_method(name, args)) + + clone_methods.append(gen_clone_method(name, args)) + + contents = "#define CACHE_IR_OPS(_)\\\n" + contents += "\\\n".join(ops_items) + contents += "\n\n" + + contents += "#define CACHE_IR_WRITER_GENERATED \\\n" + contents += "\\\n".join(writer_methods) + contents += "\n\n" + + contents += "#define CACHE_IR_COMPILER_SHARED_GENERATED \\\n" + contents += "\\\n".join(compiler_shared_methods) + contents += "\n\n" + + contents += "#define CACHE_IR_COMPILER_UNSHARED_GENERATED \\\n" + contents += "\\\n".join(compiler_unshared_methods) + contents += "\n\n" + + contents += "#define CACHE_IR_TRANSPILER_GENERATED \\\n" + contents += "\\\n".join(transpiler_methods) + contents += "\n\n" + + contents += "#define CACHE_IR_TRANSPILER_OPS(_)\\\n" + contents += "\\\n".join(transpiler_ops) + contents += "\n\n" + + contents += "#define CACHE_IR_SPEWER_GENERATED \\\n" + contents += "\\\n".join(spewer_methods) + contents += "\n\n" + + contents += "#define CACHE_IR_CLONE_GENERATED \\\n" + contents += "\\\n".join(clone_methods) + contents += "\n\n" + + generate_header(c_out, "jit_CacheIROpsGenerated_h", contents) |