import buildconfig import six 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/GenerateABIFunctionType.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() return yaml.safe_load(contents) def cpp_arg_type(arg_type): if arg_type == "General": return "intptr_t" elif arg_type == "Int32": return "int32_t" elif arg_type == "Int64": return "int64_t" elif arg_type == "Float32": return "float" elif arg_type == "Float64": return "double" else: raise ValueError(arg_type) def func_type_name(func_type): if "name" in func_type: return func_type["name"] # Autogenerate a name like `General_GeneralGeneral` if none is specified return f"{func_type['ret']}_{''.join(func_type['args'])}" def func_type_has_floats(func_type): for arg in func_type["args"]: if arg == "Float32" or arg == "Float64": return True return False # Generate the ARM32 argument loading for a func type for when soft-FP is enabled def arm32_soft_fp_args(func_type): # This must match ABIArgGenerator::softNext() in Assembler-arm.cpp contents = "" numIntArgRegs = 4 intRegIndex = 0 stackOffset = 0 for i, arg in enumerate(func_type["args"]): if i != 0: contents += ", " if arg == "General": if intRegIndex == numIntArgRegs: contents += f"stack_pointer[{stackOffset}]" stackOffset += 1 else: contents += f"a{intRegIndex}" intRegIndex += 1 elif arg == "Int32": if intRegIndex == numIntArgRegs: contents += f"stack_pointer[{stackOffset}]" stackOffset += 1 else: contents += f"a{intRegIndex}" intRegIndex += 1 elif arg == "Int64": intRegIndex += intRegIndex % 2 if intRegIndex == numIntArgRegs: stackOffset += stackOffset % 2 contents += f"MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}])" stackOffset += 2 else: contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex+1})" intRegIndex += 2 elif arg == "Float32": if intRegIndex == numIntArgRegs: contents += f"mozilla::BitwiseCast(stack_pointer[{stackOffset}])" stackOffset += 1 else: contents += f"mozilla::BitwiseCast(a{intRegIndex})" intRegIndex += 1 elif arg == "Float64": intRegIndex += intRegIndex % 2 if intRegIndex == numIntArgRegs: stackOffset += stackOffset % 2 contents += f"mozilla::BitwiseCast(MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}]))" stackOffset += 2 else: contents += f"mozilla::BitwiseCast(MakeInt64(a{intRegIndex}, a{intRegIndex + 1}))" intRegIndex += 2 assert intRegIndex <= numIntArgRegs return contents # Generate the ARM32 argument loading for a func type for when hard-FP is enabled def arm32_hard_fp_args(func_type): # This must match ABIArgGenerator::hardNext() in Assembler-arm.cpp contents = "" numIntArgRegs = 4 numFloatArgRegs = 16 intRegIndex = 0 floatRegIndex = 0 stackOffset = 0 for i, arg in enumerate(func_type["args"]): if i != 0: contents += ", " if arg == "General": if intRegIndex == numIntArgRegs: contents += f"stack_pointer[{stackOffset}]" stackOffset += 1 else: contents += f"a{intRegIndex}" intRegIndex += 1 elif arg == "Int32": if intRegIndex == numIntArgRegs: contents += f"stack_pointer[{stackOffset}]" stackOffset += 1 else: contents += f"a{intRegIndex}" intRegIndex += 1 elif arg == "Int64": intRegIndex += intRegIndex % 2 if intRegIndex == numIntArgRegs: stackOffset += stackOffset % 2 contents += f"MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}])" stackOffset += 2 else: contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex+1})" intRegIndex += 2 elif arg == "Float32": if floatRegIndex == numFloatArgRegs: contents += f"mozilla::BitwiseCast(stack_pointer[{stackOffset}])" stackOffset += 1 else: contents += f"s{floatRegIndex}" floatRegIndex += 1 elif arg == "Float64": floatRegIndex += floatRegIndex % 2 if floatRegIndex == numFloatArgRegs: stackOffset += stackOffset % 2 contents += f"mozilla::BitwiseCast(MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}]))" stackOffset += 2 else: contents += f"d{round(floatRegIndex / 2)}" floatRegIndex += 2 assert intRegIndex <= numIntArgRegs assert floatRegIndex <= numFloatArgRegs return contents def arm32_simulator_dispatch(func_types): contents = "" for func_type in func_types: hard_fp_args = arm32_hard_fp_args(func_type) soft_fp_args = arm32_soft_fp_args(func_type) ret = func_type["ret"] contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" contents += f" auto target = reinterpret_cast(external);\\\n" contents += f" {cpp_arg_type(ret)} ret;\\\n" if func_type_has_floats(func_type): contents += " if (UseHardFpABI()) {\\\n" contents += f" ret = target({hard_fp_args});\\\n" contents += " } else {\\\n" contents += f" ret = target({soft_fp_args});\\\n" contents += " }\\\n" else: # No float args means we don't need to check the float ABI and # either generated args will do. contents += f" ret = target({soft_fp_args});\\\n" contents += " scratchVolatileRegisters((void*)target);\\\n" if ret == "General" or ret == "Int32" or ret == "Int64": contents += " setCallResult(ret);\\\n" elif ret == "Float32": contents += " setCallResultFloat(ret);\\\n" elif ret == "Float64": contents += " setCallResultDouble(ret);\\\n" contents += " break;\\\n" contents += "}\\\n" return contents # Generate the ARM64 argument loading for a func type def arm64_args(func_type): # This must match ABIArgGenerator::next() in Assembler-arm64.cpp contents = "" numIntArgRegs = 8 numFloatArgRegs = 8 intRegIndex = 0 floatRegIndex = 0 stackOffset = 0 for i, arg in enumerate(func_type["args"]): if i != 0: contents += ", " if arg == "General": if intRegIndex == numIntArgRegs: contents += f"sp[{stackOffset}]" stackOffset += 1 else: contents += f"x{intRegIndex}" intRegIndex += 1 elif arg == "Int32": if intRegIndex == numIntArgRegs: contents += f"sp[{stackOffset}]" stackOffset += 1 else: contents += f"x{intRegIndex}" intRegIndex += 1 elif arg == "Int64": if intRegIndex == numIntArgRegs: contents += f"sp[{stackOffset}]" stackOffset += 1 else: contents += f"x{intRegIndex}" intRegIndex += 1 elif arg == "Float32": if floatRegIndex == numFloatArgRegs: contents += f"mozilla::BitwiseCast(sp[{stackOffset}])" stackOffset += 1 else: contents += f"s{floatRegIndex}" floatRegIndex += 1 elif arg == "Float64": if floatRegIndex == numFloatArgRegs: contents += f"mozilla::BitwiseCast(sp[{stackOffset}])" stackOffset += 1 else: contents += f"d{floatRegIndex}" floatRegIndex += 1 assert intRegIndex <= numIntArgRegs assert floatRegIndex <= numFloatArgRegs return contents def arm64_simulator_dispatch(func_types): contents = "" for func_type in func_types: args = arm64_args(func_type) contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" contents += f" auto target = reinterpret_cast(nativeFn);\\\n" contents += f" auto ret = target({args});\\\n" ret = func_type["ret"] if ret == "General": contents += " setGPR64Result(ret);\\\n" elif ret == "Int32": contents += " setGPR32Result(ret);\\\n" elif ret == "Int64": contents += " setGPR64Result(ret);\\\n" elif ret == "Float32": contents += " setFP32Result(ret);\\\n" elif ret == "Float64": contents += " setFP64Result(ret);\\\n" contents += " break;\\\n" contents += "}\\\n" return contents # Generate the LoongArch64 argument loading for a func type def loongarch64_args(func_type): # This must match ABIArgGenerator::next() in Assembler-loong64.cpp contents = "" numIntArgRegs = 8 numFloatArgRegs = 8 intRegIndex = 0 floatRegIndex = 0 stackOffset = 0 for i, arg in enumerate(func_type["args"]): if i != 0: contents += ", " if arg == "General": if intRegIndex == numIntArgRegs: contents += f"sp_[{stackOffset}]" stackOffset += 1 else: contents += f"a{intRegIndex}_" intRegIndex += 1 elif arg == "Int32": if intRegIndex == numIntArgRegs: contents += f"I32(sp_[{stackOffset}])" stackOffset += 1 else: contents += f"I32(a{intRegIndex}_)" intRegIndex += 1 elif arg == "Int64": if intRegIndex == numIntArgRegs: contents += f"sp_[{stackOffset}]" stackOffset += 1 else: contents += f"a{intRegIndex}_" intRegIndex += 1 elif arg == "Float32": if floatRegIndex == numFloatArgRegs: contents += f"*mozilla::BitwiseCast(sp_[{stackOffset}])" stackOffset += 1 else: contents += f"f{floatRegIndex}_s" floatRegIndex += 1 elif arg == "Float64": if floatRegIndex == numFloatArgRegs: contents += f"mozilla::BitwiseCast(sp_[{stackOffset}])" stackOffset += 1 else: contents += f"f{floatRegIndex}_d" floatRegIndex += 1 assert intRegIndex <= numIntArgRegs assert floatRegIndex <= numFloatArgRegs return contents def loongarch64_simulator_dispatch(func_types): contents = "" for func_type in func_types: args = loongarch64_args(func_type) contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" contents += f" auto target = reinterpret_cast(nativeFn);\\\n" contents += f" auto ret = target({args});\\\n" ret = func_type["ret"] if ret == "General": contents += " setCallResult(ret);\\\n" elif ret == "Int32": contents += " setCallResult(I64(ret));\\\n" elif ret == "Int64": contents += " setCallResult(ret);\\\n" elif ret == "Float32": contents += " setCallResultFloat(ret);\\\n" elif ret == "Float64": contents += " setCallResultDouble(ret);\\\n" contents += " break;\\\n" contents += "}\\\n" return contents # Generate the MIPS64 argument loading for a func type def mips64_args(func_type): # This must match ABIArgGenerator::next() in Assembler-mips64.cpp contents = "" numIntArgRegs = 8 numFloatArgRegs = 8 regIndex = 0 stackOffset = 0 for i, arg in enumerate(func_type["args"]): if i != 0: contents += ", " if arg == "General": if regIndex == numIntArgRegs: contents += f"sp_[{stackOffset}]" stackOffset += 1 else: contents += f"a{regIndex}_" regIndex += 1 elif arg == "Int32": if regIndex == numIntArgRegs: contents += f"I32(sp_[{stackOffset}])" stackOffset += 1 else: contents += f"I32(a{regIndex}_)" regIndex += 1 elif arg == "Int64": if regIndex == numIntArgRegs: contents += f"sp_[{stackOffset}]" stackOffset += 1 else: contents += f"a{regIndex}_" regIndex += 1 elif arg == "Float32": if regIndex == numFloatArgRegs: contents += f"*mozilla::BitwiseCast(sp_[{stackOffset}])" stackOffset += 1 else: contents += f"f{regIndex + 12}_s" regIndex += 1 elif arg == "Float64": if regIndex == numFloatArgRegs: contents += f"mozilla::BitwiseCast(sp_[{stackOffset}])" stackOffset += 1 else: contents += f"f{regIndex + 12}_d" regIndex += 1 assert regIndex <= numIntArgRegs assert numIntArgRegs == numFloatArgRegs return contents def mips64_simulator_dispatch(func_types): contents = "" for func_type in func_types: args = mips64_args(func_type) contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" contents += f" auto target = reinterpret_cast(nativeFn);\\\n" contents += f" auto ret = target({args});\\\n" ret = func_type["ret"] if ret == "General": contents += " setCallResult(ret);\\\n" elif ret == "Int32": contents += " setCallResult(I64(ret));\\\n" elif ret == "Int64": contents += " setCallResult(ret);\\\n" elif ret == "Float32": contents += " setCallResultFloat(ret);\\\n" elif ret == "Float64": contents += " setCallResultDouble(ret);\\\n" contents += " break;\\\n" contents += "}\\\n" return contents def main(c_out, yaml_path): func_types = load_yaml(yaml_path) # Define the ABIFunctionType enum contents = "#define ABI_FUNCTION_TYPE_ENUM \\\n" for func_type in func_types: name = "Args_" + func_type_name(func_type) args = ", ".join(f"ABIType::{p}" for p in func_type["args"]) ret = f"ABIType::{func_type['ret']}" contents += f" {name} = detail::MakeABIFunctionType({ret}, {{{args}}}),\\\n" contents += "\n" # Define the prototypes of the types contents += "#define ABI_FUNCTION_TYPE_SIM_PROTOTYPES \\\n" for func_type in func_types: name = "Prototype_" + func_type_name(func_type) args = ", ".join(cpp_arg_type(p) for p in func_type["args"]) ret = cpp_arg_type(func_type["ret"]) contents += f" typedef {ret} (*{name})({args});\\\n" contents += "\n" contents += "#define ABI_FUNCTION_TYPE_ARM64_SIM_DISPATCH \\\n" contents += arm64_simulator_dispatch(func_types) contents += "\n" contents += "#define ABI_FUNCTION_TYPE_ARM32_SIM_DISPATCH \\\n" contents += arm32_simulator_dispatch(func_types) contents += "\n" contents += "#define ABI_FUNCTION_TYPE_LOONGARCH64_SIM_DISPATCH \\\n" contents += loongarch64_simulator_dispatch(func_types) contents += "\n" contents += "#define ABI_FUNCTION_TYPE_MIPS64_SIM_DISPATCH \\\n" contents += mips64_simulator_dispatch(func_types) contents += "\n" generate_header(c_out, "jit_ABIFunctionTypeGenerated_h", contents)