GenerateABIFunctionType.py (20944B)
1 import io 2 3 import buildconfig 4 import yaml 5 from mozbuild.preprocessor import Preprocessor 6 7 HEADER_TEMPLATE = """\ 8 /* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12 #ifndef %(includeguard)s 13 #define %(includeguard)s 14 15 /* This file is generated by jit/GenerateABIFunctionType.py. Do not edit! */ 16 17 %(contents)s 18 19 #endif // %(includeguard)s 20 """ 21 22 23 def generate_header(c_out, includeguard, contents): 24 c_out.write( 25 HEADER_TEMPLATE 26 % { 27 "includeguard": includeguard, 28 "contents": contents, 29 } 30 ) 31 32 33 def load_yaml(yaml_path): 34 # First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in 35 # the YAML file. 36 pp = Preprocessor() 37 pp.context.update(buildconfig.defines["ALLDEFINES"]) 38 pp.out = io.StringIO() 39 pp.do_filter("substitution") 40 pp.do_include(yaml_path) 41 contents = pp.out.getvalue() 42 return yaml.safe_load(contents) 43 44 45 def cpp_arg_type(arg_type): 46 if arg_type == "General": 47 return "intptr_t" 48 elif arg_type == "Int32": 49 return "int32_t" 50 elif arg_type == "Int64": 51 return "int64_t" 52 elif arg_type == "Float32": 53 return "float" 54 elif arg_type == "Float64": 55 return "double" 56 elif arg_type == "Void": 57 return "void" 58 else: 59 raise ValueError(arg_type) 60 61 62 def func_type_name(func_type): 63 if "name" in func_type: 64 return func_type["name"] 65 66 # Autogenerate a name like `General_GeneralGeneral` if none is specified 67 return f"{func_type['ret']}_{''.join(func_type['args'])}" 68 69 70 def func_type_has_floats(func_type): 71 for arg in func_type["args"]: 72 if arg in {"Float32", "Float64"}: 73 return True 74 return False 75 76 77 # Generate the ARM32 argument loading for a func type for when soft-FP is enabled 78 def arm32_soft_fp_args(func_type): 79 # This must match ABIArgGenerator::softNext() in Assembler-arm.cpp 80 contents = "" 81 numIntArgRegs = 4 82 intRegIndex = 0 83 stackOffset = 0 84 for i, arg in enumerate(func_type["args"]): 85 if i != 0: 86 contents += ", " 87 88 if arg == "General": 89 if intRegIndex == numIntArgRegs: 90 contents += f"stack_pointer[{stackOffset}]" 91 stackOffset += 1 92 else: 93 contents += f"a{intRegIndex}" 94 intRegIndex += 1 95 elif arg == "Int32": 96 if intRegIndex == numIntArgRegs: 97 contents += f"stack_pointer[{stackOffset}]" 98 stackOffset += 1 99 else: 100 contents += f"a{intRegIndex}" 101 intRegIndex += 1 102 elif arg == "Int64": 103 intRegIndex += intRegIndex % 2 104 if intRegIndex == numIntArgRegs: 105 stackOffset += stackOffset % 2 106 contents += f"MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}])" 107 stackOffset += 2 108 else: 109 contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex + 1})" 110 intRegIndex += 2 111 elif arg == "Float32": 112 if intRegIndex == numIntArgRegs: 113 contents += f"mozilla::BitwiseCast<float>(stack_pointer[{stackOffset}])" 114 stackOffset += 1 115 else: 116 contents += f"mozilla::BitwiseCast<float>(a{intRegIndex})" 117 intRegIndex += 1 118 elif arg == "Float64": 119 intRegIndex += intRegIndex % 2 120 if intRegIndex == numIntArgRegs: 121 stackOffset += stackOffset % 2 122 contents += f"mozilla::BitwiseCast<double>(MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}]))" 123 stackOffset += 2 124 else: 125 contents += f"mozilla::BitwiseCast<double>(MakeInt64(a{intRegIndex}, a{intRegIndex + 1}))" 126 intRegIndex += 2 127 assert intRegIndex <= numIntArgRegs 128 return contents 129 130 131 # Generate the ARM32 argument loading for a func type for when hard-FP is enabled 132 def arm32_hard_fp_args(func_type): 133 # This must match ABIArgGenerator::hardNext() in Assembler-arm.cpp 134 contents = "" 135 numIntArgRegs = 4 136 numFloatArgRegs = 16 137 intRegIndex = 0 138 floatRegIndex = 0 139 stackOffset = 0 140 for i, arg in enumerate(func_type["args"]): 141 if i != 0: 142 contents += ", " 143 144 if arg == "General": 145 if intRegIndex == numIntArgRegs: 146 contents += f"stack_pointer[{stackOffset}]" 147 stackOffset += 1 148 else: 149 contents += f"a{intRegIndex}" 150 intRegIndex += 1 151 elif arg == "Int32": 152 if intRegIndex == numIntArgRegs: 153 contents += f"stack_pointer[{stackOffset}]" 154 stackOffset += 1 155 else: 156 contents += f"a{intRegIndex}" 157 intRegIndex += 1 158 elif arg == "Int64": 159 intRegIndex += intRegIndex % 2 160 if intRegIndex == numIntArgRegs: 161 stackOffset += stackOffset % 2 162 contents += f"MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}])" 163 stackOffset += 2 164 else: 165 contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex + 1})" 166 intRegIndex += 2 167 elif arg == "Float32": 168 if floatRegIndex == numFloatArgRegs: 169 contents += f"mozilla::BitwiseCast<float>(stack_pointer[{stackOffset}])" 170 stackOffset += 1 171 else: 172 contents += f"s{floatRegIndex}" 173 floatRegIndex += 1 174 elif arg == "Float64": 175 floatRegIndex += floatRegIndex % 2 176 if floatRegIndex == numFloatArgRegs: 177 stackOffset += stackOffset % 2 178 contents += f"mozilla::BitwiseCast<double>(MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}]))" 179 stackOffset += 2 180 else: 181 contents += f"d{round(floatRegIndex / 2)}" 182 floatRegIndex += 2 183 assert intRegIndex <= numIntArgRegs 184 assert floatRegIndex <= numFloatArgRegs 185 return contents 186 187 188 def arm32_simulator_dispatch(func_types): 189 contents = "" 190 for func_type in func_types: 191 hard_fp_args = arm32_hard_fp_args(func_type) 192 soft_fp_args = arm32_soft_fp_args(func_type) 193 ret = func_type["ret"] 194 has_ret = ret != "Void" 195 ret_setter = "ret = " if has_ret else "" 196 197 contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" 198 contents += f" auto target = reinterpret_cast<Prototype_{func_type_name(func_type)}>(external);\\\n" 199 if has_ret: 200 contents += f" {cpp_arg_type(ret)} ret;\\\n" 201 if func_type_has_floats(func_type): 202 contents += " if (ARMFlags::UseHardFpABI()) {\\\n" 203 contents += f" {ret_setter}target({hard_fp_args});\\\n" 204 contents += " } else {\\\n" 205 contents += f" {ret_setter}target({soft_fp_args});\\\n" 206 contents += " }\\\n" 207 else: 208 # No float args means we don't need to check the float ABI and 209 # either generated args will do. 210 contents += f" {ret_setter}target({soft_fp_args});\\\n" 211 contents += " scratchVolatileRegisters((void*)target);\\\n" 212 if ret in {"General", "Int32", "Int64"}: 213 contents += " setCallResult(ret);\\\n" 214 elif ret == "Float32": 215 contents += " setCallResultFloat(ret);\\\n" 216 elif ret == "Float64": 217 contents += " setCallResultDouble(ret);\\\n" 218 contents += " break;\\\n" 219 contents += "}\\\n" 220 return contents 221 222 223 # Generate the ARM64 argument loading for a func type 224 def arm64_args(func_type): 225 # This must match ABIArgGenerator::next() in Assembler-arm64.cpp 226 contents = "" 227 numIntArgRegs = 8 228 numFloatArgRegs = 8 229 intRegIndex = 0 230 floatRegIndex = 0 231 stackOffset = 0 232 for i, arg in enumerate(func_type["args"]): 233 if i != 0: 234 contents += ", " 235 236 if arg == "General": 237 if intRegIndex == numIntArgRegs: 238 contents += f"sp[{stackOffset}]" 239 stackOffset += 1 240 else: 241 contents += f"x{intRegIndex}" 242 intRegIndex += 1 243 elif arg == "Int32": 244 if intRegIndex == numIntArgRegs: 245 contents += f"sp[{stackOffset}]" 246 stackOffset += 1 247 else: 248 contents += f"x{intRegIndex}" 249 intRegIndex += 1 250 elif arg == "Int64": 251 if intRegIndex == numIntArgRegs: 252 contents += f"sp[{stackOffset}]" 253 stackOffset += 1 254 else: 255 contents += f"x{intRegIndex}" 256 intRegIndex += 1 257 elif arg == "Float32": 258 if floatRegIndex == numFloatArgRegs: 259 contents += f"mozilla::BitwiseCast<float>(sp[{stackOffset}])" 260 stackOffset += 1 261 else: 262 contents += f"s{floatRegIndex}" 263 floatRegIndex += 1 264 elif arg == "Float64": 265 if floatRegIndex == numFloatArgRegs: 266 contents += f"mozilla::BitwiseCast<double>(sp[{stackOffset}])" 267 stackOffset += 1 268 else: 269 contents += f"d{floatRegIndex}" 270 floatRegIndex += 1 271 assert intRegIndex <= numIntArgRegs 272 assert floatRegIndex <= numFloatArgRegs 273 return contents 274 275 276 def arm64_simulator_dispatch(func_types): 277 contents = "" 278 for func_type in func_types: 279 args = arm64_args(func_type) 280 contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" 281 contents += f" auto target = reinterpret_cast<Prototype_{func_type_name(func_type)}>(nativeFn);\\\n" 282 ret = func_type["ret"] 283 if ret == "Void": 284 contents += f" target({args});\\\n" 285 else: 286 contents += f" auto ret = target({args});\\\n" 287 if ret == "General": 288 contents += " setGPR64Result(ret);\\\n" 289 elif ret == "Int32": 290 contents += " setGPR32Result(ret);\\\n" 291 elif ret == "Int64": 292 contents += " setGPR64Result(ret);\\\n" 293 elif ret == "Float32": 294 contents += " setFP32Result(ret);\\\n" 295 elif ret == "Float64": 296 contents += " setFP64Result(ret);\\\n" 297 contents += " break;\\\n" 298 contents += "}\\\n" 299 return contents 300 301 302 # Generate the LoongArch64 argument loading for a func type 303 def loongarch64_args(func_type): 304 # This must match ABIArgGenerator::next() in Assembler-loong64.cpp 305 contents = "" 306 numIntArgRegs = 8 307 numFloatArgRegs = 8 308 intRegIndex = 0 309 floatRegIndex = 0 310 stackOffset = 0 311 for i, arg in enumerate(func_type["args"]): 312 if i != 0: 313 contents += ", " 314 315 if arg == "General": 316 if intRegIndex == numIntArgRegs: 317 contents += f"sp_[{stackOffset}]" 318 stackOffset += 1 319 else: 320 contents += f"a{intRegIndex}_" 321 intRegIndex += 1 322 elif arg == "Int32": 323 if intRegIndex == numIntArgRegs: 324 contents += f"I32(sp_[{stackOffset}])" 325 stackOffset += 1 326 else: 327 contents += f"I32(a{intRegIndex}_)" 328 intRegIndex += 1 329 elif arg == "Int64": 330 if intRegIndex == numIntArgRegs: 331 contents += f"sp_[{stackOffset}]" 332 stackOffset += 1 333 else: 334 contents += f"a{intRegIndex}_" 335 intRegIndex += 1 336 elif arg == "Float32": 337 if floatRegIndex == numFloatArgRegs: 338 contents += f"*mozilla::BitwiseCast<float*>(sp_[{stackOffset}])" 339 stackOffset += 1 340 else: 341 contents += f"f{floatRegIndex}_s" 342 floatRegIndex += 1 343 elif arg == "Float64": 344 if floatRegIndex == numFloatArgRegs: 345 contents += f"mozilla::BitwiseCast<double>(sp_[{stackOffset}])" 346 stackOffset += 1 347 else: 348 contents += f"f{floatRegIndex}_d" 349 floatRegIndex += 1 350 assert intRegIndex <= numIntArgRegs 351 assert floatRegIndex <= numFloatArgRegs 352 return contents 353 354 355 def loongarch64_simulator_dispatch(func_types): 356 contents = "" 357 for func_type in func_types: 358 args = loongarch64_args(func_type) 359 contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" 360 contents += f" auto target = reinterpret_cast<Prototype_{func_type_name(func_type)}>(nativeFn);\\\n" 361 ret = func_type["ret"] 362 if ret == "Void": 363 contents += f" target({args});\\\n" 364 else: 365 contents += f" auto ret = target({args});\\\n" 366 if ret == "General": 367 contents += " setCallResult(ret);\\\n" 368 elif ret == "Int32": 369 contents += " setCallResult(I64(ret));\\\n" 370 elif ret == "Int64": 371 contents += " setCallResult(ret);\\\n" 372 elif ret == "Float32": 373 contents += " setCallResultFloat(ret);\\\n" 374 elif ret == "Float64": 375 contents += " setCallResultDouble(ret);\\\n" 376 contents += " break;\\\n" 377 contents += "}\\\n" 378 return contents 379 380 381 # Generate the MIPS64 argument loading for a func type 382 def mips64_args(func_type): 383 # This must match ABIArgGenerator::next() in Assembler-mips64.cpp 384 contents = "" 385 numIntArgRegs = 8 386 numFloatArgRegs = 8 387 regIndex = 0 388 stackOffset = 0 389 for i, arg in enumerate(func_type["args"]): 390 if i != 0: 391 contents += ", " 392 393 if arg == "General": 394 if regIndex == numIntArgRegs: 395 contents += f"sp_[{stackOffset}]" 396 stackOffset += 1 397 else: 398 contents += f"a{regIndex}_" 399 regIndex += 1 400 elif arg == "Int32": 401 if regIndex == numIntArgRegs: 402 contents += f"I32(sp_[{stackOffset}])" 403 stackOffset += 1 404 else: 405 contents += f"I32(a{regIndex}_)" 406 regIndex += 1 407 elif arg == "Int64": 408 if regIndex == numIntArgRegs: 409 contents += f"sp_[{stackOffset}]" 410 stackOffset += 1 411 else: 412 contents += f"a{regIndex}_" 413 regIndex += 1 414 elif arg == "Float32": 415 if regIndex == numFloatArgRegs: 416 contents += f"*mozilla::BitwiseCast<float*>(sp_[{stackOffset}])" 417 stackOffset += 1 418 else: 419 contents += f"f{regIndex + 12}_s" 420 regIndex += 1 421 elif arg == "Float64": 422 if regIndex == numFloatArgRegs: 423 contents += f"mozilla::BitwiseCast<double>(sp_[{stackOffset}])" 424 stackOffset += 1 425 else: 426 contents += f"f{regIndex + 12}_d" 427 regIndex += 1 428 assert regIndex <= numIntArgRegs 429 assert numIntArgRegs == numFloatArgRegs 430 return contents 431 432 433 def mips64_simulator_dispatch(func_types): 434 contents = "" 435 for func_type in func_types: 436 args = mips64_args(func_type) 437 contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" 438 contents += f" auto target = reinterpret_cast<Prototype_{func_type_name(func_type)}>(nativeFn);\\\n" 439 ret = func_type["ret"] 440 if ret == "Void": 441 contents += f" target({args});\\\n" 442 else: 443 contents += f" auto ret = target({args});\\\n" 444 if ret == "General": 445 contents += " setCallResult(ret);\\\n" 446 elif ret == "Int32": 447 contents += " setCallResult(I64(ret));\\\n" 448 elif ret == "Int64": 449 contents += " setCallResult(ret);\\\n" 450 elif ret == "Float32": 451 contents += " setCallResultFloat(ret);\\\n" 452 elif ret == "Float64": 453 contents += " setCallResultDouble(ret);\\\n" 454 contents += " break;\\\n" 455 contents += "}\\\n" 456 return contents 457 458 459 def riscv64_args(func_type): 460 # This must match ABIArgGenerator::next() in Assembler-riscv64.cpp 461 contents = "" 462 numIntArgRegs = 8 463 numFloatArgRegs = 8 464 regIndex = 0 465 floatRegIndex = 0 466 stackOffset = 0 467 for i, arg in enumerate(func_type["args"]): 468 if i != 0: 469 contents += ", " 470 471 if arg == "General": 472 if regIndex == numIntArgRegs: 473 contents += f"sp_[{stackOffset}]" 474 stackOffset += 1 475 else: 476 contents += f"arg{regIndex}" 477 regIndex += 1 478 elif arg == "Int32": 479 if regIndex == numIntArgRegs: 480 contents += f"I32(sp_[{stackOffset}])" 481 stackOffset += 1 482 else: 483 contents += f"I32(arg{regIndex})" 484 regIndex += 1 485 elif arg == "Int64": 486 if regIndex == numIntArgRegs: 487 contents += f"sp_[{stackOffset}]" 488 stackOffset += 1 489 else: 490 contents += f"arg{regIndex}" 491 regIndex += 1 492 elif arg == "Float32": 493 if floatRegIndex == numFloatArgRegs: 494 contents += f"mozilla::BitwiseCast<float>(static_cast<uint32_t>(sp_[{stackOffset}]))" 495 stackOffset += 1 496 else: 497 contents += f"getFpuRegisterFloat(fa{floatRegIndex})" 498 floatRegIndex += 1 499 elif arg == "Float64": 500 if floatRegIndex == numFloatArgRegs: 501 contents += f"mozilla::BitwiseCast<double>(static_cast<uint64_t>(sp_[{stackOffset}]))" 502 stackOffset += 1 503 else: 504 contents += f"getFpuRegisterDouble(fa{floatRegIndex})" 505 floatRegIndex += 1 506 else: 507 raise ValueError(f"Unknown arg type: {arg}") 508 return contents 509 510 511 def riscv64_simulator_dispatch(func_types): 512 contents = "" 513 for func_type in func_types: 514 args = riscv64_args(func_type) 515 contents += f"case js::jit::Args_{func_type_name(func_type)}: {{\\\n" 516 contents += f" auto target = reinterpret_cast<Prototype_{func_type_name(func_type)}>(nativeFn);\\\n" 517 ret = func_type["ret"] 518 if ret == "Void": 519 contents += f" target({args});\\\n" 520 else: 521 contents += f" auto ret = target({args});\\\n" 522 if ret == "Void": 523 pass 524 elif ret == "General": 525 contents += " setCallResult(ret);\\\n" 526 elif ret == "Int32": 527 contents += " setCallResult(I64(ret));\\\n" 528 elif ret == "Int64": 529 contents += " setCallResult(ret);\\\n" 530 elif ret == "Float32": 531 contents += " setCallResultFloat(ret);\\\n" 532 elif ret == "Float64": 533 contents += " setCallResultDouble(ret);\\\n" 534 else: 535 raise ValueError(f"Unknown ret type: {ret}") 536 contents += " break;\\\n" 537 contents += "}\\\n" 538 return contents 539 540 541 def main(c_out, yaml_path): 542 func_types = load_yaml(yaml_path) 543 544 # Define the ABIFunctionType enum 545 contents = "#define ABI_FUNCTION_TYPE_ENUM \\\n" 546 for func_type in func_types: 547 name = "Args_" + func_type_name(func_type) 548 args = ", ".join(f"ABIType::{p}" for p in func_type["args"]) 549 ret = f"ABIType::{func_type['ret']}" 550 551 contents += f" {name} = detail::MakeABIFunctionType({ret}, {{{args}}}),\\\n" 552 contents += "\n" 553 554 # Define the prototypes of the types 555 contents += "#define ABI_FUNCTION_TYPE_SIM_PROTOTYPES \\\n" 556 for func_type in func_types: 557 name = "Prototype_" + func_type_name(func_type) 558 args = ", ".join(cpp_arg_type(p) for p in func_type["args"]) 559 ret = cpp_arg_type(func_type["ret"]) 560 561 contents += f" typedef {ret} (*{name})({args});\\\n" 562 contents += "\n" 563 564 contents += "#define ABI_FUNCTION_TYPE_ARM64_SIM_DISPATCH \\\n" 565 contents += arm64_simulator_dispatch(func_types) 566 contents += "\n" 567 568 contents += "#define ABI_FUNCTION_TYPE_ARM32_SIM_DISPATCH \\\n" 569 contents += arm32_simulator_dispatch(func_types) 570 contents += "\n" 571 572 contents += "#define ABI_FUNCTION_TYPE_LOONGARCH64_SIM_DISPATCH \\\n" 573 contents += loongarch64_simulator_dispatch(func_types) 574 contents += "\n" 575 576 contents += "#define ABI_FUNCTION_TYPE_MIPS64_SIM_DISPATCH \\\n" 577 contents += mips64_simulator_dispatch(func_types) 578 contents += "\n" 579 580 contents += "#define ABI_FUNCTION_TYPE_RISCV64_SIM_DISPATCH \\\n" 581 contents += riscv64_simulator_dispatch(func_types) 582 contents += "\n" 583 584 generate_header(c_out, "jit_ABIFunctionTypeGenerated_h", contents)