GenerateLIRFiles.py (16570B)
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 8 import io 9 from itertools import groupby 10 from operator import itemgetter 11 12 import buildconfig 13 import yaml 14 from mozbuild.preprocessor import Preprocessor 15 16 HEADER_TEMPLATE = """\ 17 /* This Source Code Form is subject to the terms of the Mozilla Public 18 * License, v. 2.0. If a copy of the MPL was not distributed with this 19 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 20 21 #ifndef %(includeguard)s 22 #define %(includeguard)s 23 24 /* This file is generated by jit/GenerateLIRFiles.py. Do not edit! */ 25 26 %(contents)s 27 28 #endif // %(includeguard)s 29 """ 30 31 32 def load_yaml(yaml_path): 33 # First invoke preprocessor.py so that we can use #ifdef JS_SIMULATOR in 34 # the YAML file. 35 pp = Preprocessor() 36 pp.context.update(buildconfig.defines["ALLDEFINES"]) 37 pp.out = io.StringIO() 38 pp.do_filter("substitution") 39 pp.do_include(yaml_path) 40 contents = pp.out.getvalue() 41 return yaml.safe_load(contents) 42 43 44 def generate_header(c_out, includeguard, contents): 45 c_out.write( 46 HEADER_TEMPLATE 47 % { 48 "includeguard": includeguard, 49 "contents": contents, 50 } 51 ) 52 53 54 operand_types = { 55 "WordSized": "LAllocation", 56 "BoxedValue": "LBoxAllocation", 57 "Int64": "LInt64Allocation", 58 } 59 60 61 result_types = { 62 "WordSized": "1", 63 "BoxedValue": "BOX_PIECES", 64 "Int64": "INT64_PIECES", 65 } 66 67 68 # Generate the index expression for a BoxedValue operand. 69 # 70 # The expression has the form |num_operands + index * BOX_PIECES|, with zero 71 # terms being omitted. 72 def make_boxed_index(index, reg_operands): 73 num_operands = len(reg_operands) 74 75 expr = [] 76 if num_operands: 77 expr.append(f"{num_operands}") 78 if index: 79 expr.append(f"{index} * BOX_PIECES") 80 return " + ".join(expr) if expr else "0" 81 82 83 # Generate the index expression for an Int64 operand. 84 # 85 # The expression has the form 86 # |num_operands + num_value_operands * BOX_PIECES + index * INT64_PIECES|, with 87 # zero terms being omitted. 88 def make_int64_index(index, reg_operands, value_operands): 89 num_operands = len(reg_operands) 90 num_value_operands = len(value_operands) 91 92 expr = [] 93 if num_operands: 94 expr.append(f"{num_operands}") 95 if num_value_operands: 96 expr.append(f"{num_value_operands} * BOX_PIECES") 97 if index: 98 expr.append(f"{index} * INT64_PIECES") 99 return " + ".join(expr) if expr else "0" 100 101 102 def gen_operands(operands, defer_init): 103 # Group operands by operand type. 104 sorted_operands = { 105 k: [op for op, _ in v] 106 for k, v in groupby(sorted(operands.items(), key=itemgetter(1)), itemgetter(1)) 107 } 108 109 # Exactly three operand types are supported: WordSized, BoxedValue, and Int64. 110 if len(sorted_operands) > 3: 111 raise Exception("Invalid operand type: " + str(sorted_operands.keys())) 112 113 reg_operands = sorted_operands.get("WordSized", []) 114 value_operands = sorted_operands.get("BoxedValue", []) 115 int64_operands = sorted_operands.get("Int64", []) 116 117 # Operand index definitions. 118 indices = [] 119 120 # Parameters for the class constructor. 121 params = [] 122 123 # Initializer instructions for constructor body. 124 initializers = [] 125 126 # Getter definitions. 127 getters = [] 128 129 # Setter definitions. 130 setters = [] 131 132 # Constructor parameters are generated in the order defined in the YAML file. 133 if not defer_init: 134 for operand, op_type in operands.items(): 135 params.append(f"const {operand_types[op_type]}& {operand}") 136 137 # First initialize all word-sized operands. 138 for index, operand in enumerate(reg_operands): 139 cap_operand = operand[0].upper() + operand[1:] 140 index_value = cap_operand + "Index" 141 init_expr = f"setOperand({index_value}, {operand});" 142 143 indices.append(f"static constexpr size_t {index_value} = {index};") 144 if not defer_init: 145 initializers.append(init_expr) 146 else: 147 setters.append( 148 f"void set{cap_operand}(const LAllocation& {operand}) {{ {init_expr} }}" 149 ) 150 getters.append( 151 f"const LAllocation* {operand}() const {{ return getOperand({index_value}); }}" 152 ) 153 154 # Next initialize all BoxedValue operands. 155 for box_index, operand in enumerate(value_operands): 156 cap_operand = operand[0].upper() + operand[1:] 157 index_value = cap_operand + "Index" 158 init_expr = f"setBoxOperand({index_value}, {operand});" 159 160 indices.append( 161 f"static constexpr size_t {index_value} = {make_boxed_index(box_index, reg_operands)};" 162 ) 163 if not defer_init: 164 initializers.append(init_expr) 165 else: 166 setters.append( 167 f"void {cap_operand}(const LBoxAllocation& {operand}) {{ {init_expr} }}" 168 ) 169 getters.append( 170 f"LBoxAllocation {operand}() const {{ return getBoxOperand({index_value}); }}" 171 ) 172 173 # Finally initialize all Int64 operands. 174 for int64_index, operand in enumerate(int64_operands): 175 cap_operand = operand[0].upper() + operand[1:] 176 index_value = cap_operand + "Index" 177 init_expr = f"setInt64Operand({index_value}, {operand});" 178 179 indices.append( 180 f"static constexpr size_t {index_value} = {make_int64_index(int64_index, reg_operands, value_operands)};" 181 ) 182 if not defer_init: 183 initializers.append(init_expr) 184 else: 185 setters.append( 186 f"void set{cap_operand}(const LInt64Allocation& {operand}) {{ {init_expr} }}" 187 ) 188 getters.append( 189 f"LInt64Allocation {operand}() const {{ return getInt64Operand({index_value}); }}" 190 ) 191 192 # Total number of operands. 193 num_operands = f"{len(reg_operands)}" 194 if value_operands: 195 num_operands += f" + {len(value_operands)} * BOX_PIECES" 196 if int64_operands: 197 num_operands += f" + {len(int64_operands)} * INT64_PIECES" 198 199 return ( 200 num_operands, 201 indices, 202 params, 203 initializers, 204 getters, 205 setters, 206 ) 207 208 209 def gen_arguments(arguments): 210 # Class member definitions. 211 members = [] 212 213 # Parameters for the class constructor. 214 params = [] 215 216 # Initializer instructions for the class constructor. 217 initializers = [] 218 219 # Getter definitions. 220 getters = [] 221 222 for arg_name in arguments: 223 arg_type_sig = arguments[arg_name] 224 225 members.append(f"{arg_type_sig} {arg_name}_;") 226 params.append(f"{arg_type_sig} {arg_name}") 227 initializers.append(f"{arg_name}_({arg_name})") 228 getters.append(f"{arg_type_sig} {arg_name}() const {{ return {arg_name}_; }}") 229 230 return (members, params, initializers, getters) 231 232 233 def gen_temps(num_temps, num_temps64, defer_init): 234 # Parameters for the class constructor. 235 params = [] 236 237 # Initializer instructions for constructor body. 238 initializers = [] 239 240 # Getter definitions. 241 getters = [] 242 243 # Setter definitions. 244 setters = [] 245 246 for temp in range(num_temps): 247 param_decl = f"const LDefinition& temp{temp}" 248 init_expr = f"setTemp({temp}, temp{temp});" 249 250 if not defer_init: 251 params.append(param_decl) 252 initializers.append(init_expr) 253 else: 254 initializers.append(f"setTemp({temp}, LDefinition::BogusTemp());") 255 setters.append(f"void setTemp{temp}({param_decl}) {{ {init_expr} }}") 256 getters.append(f"const LDefinition* temp{temp}() {{ return getTemp({temp}); }}") 257 258 for int64_temp in range(num_temps64): 259 temp = num_temps + int64_temp 260 temp_index = f"{num_temps} + {int64_temp} * INT64_PIECES" 261 param_decl = f"const LInt64Definition& temp{temp}" 262 init_expr = f"setInt64Temp({temp_index}, temp{temp});" 263 264 if not defer_init: 265 params.append(param_decl) 266 initializers.append(init_expr) 267 else: 268 initializers.append(f"setTemp({temp}, LInt64Definition::BogusTemp());") 269 setters.append(f"void setTemp{temp}({param_decl}) {{ {init_expr} }}") 270 getters.append( 271 f"LInt64Definition temp{temp}() {{ return getInt64Temp({temp_index}); }}" 272 ) 273 274 # Total number of temps. 275 num_temps_total = f"{num_temps}" 276 if num_temps64: 277 num_temps_total += f" + {num_temps64} * INT64_PIECES" 278 279 return (num_temps_total, params, initializers, getters, setters) 280 281 282 def gen_successors(successors): 283 # Parameters for the class constructor. 284 params = [] 285 286 # Initializer instructions for constructor body. 287 initializers = [] 288 289 # Getter definitions. 290 getters = [] 291 292 for index, successor in enumerate(successors or []): 293 params.append(f"MBasicBlock* {successor}") 294 initializers.append(f"setSuccessor({index}, {successor});") 295 getters.append( 296 f"MBasicBlock* {successor}() const {{ return getSuccessor({index}); }}" 297 ) 298 299 return (params, initializers, getters) 300 301 302 def gen_lir_class( 303 name, 304 result_type, 305 successors, 306 operands, 307 arguments, 308 num_temps, 309 num_temps64, 310 call_instruction, 311 mir_op, 312 extra_name, 313 defer_init, 314 ): 315 """Generates class definition for a single LIR opcode.""" 316 class_name = "L" + name 317 318 ( 319 num_operands, 320 oper_indices, 321 oper_params, 322 oper_initializers, 323 oper_getters, 324 oper_setters, 325 ) = gen_operands(operands, defer_init) 326 327 args_members, args_params, args_initializers, args_getters = gen_arguments( 328 arguments 329 ) 330 331 num_temps_total, temp_params, temp_initializers, temp_getters, temp_setters = ( 332 gen_temps(num_temps, num_temps64, defer_init) 333 ) 334 335 succ_params, succ_initializers, succ_getters = gen_successors(successors) 336 337 if successors is not None: 338 if result_type: 339 raise Exception("Control instructions don't return a result") 340 num_defs = len(successors) 341 parent_class = "LControlInstructionHelper" 342 else: 343 num_defs = result_types[result_type] if result_type else "0" 344 parent_class = "LInstructionHelper" 345 346 constructor_init = "" 347 if call_instruction: 348 constructor_init += "this->setIsCall();" 349 350 mir_accessor = "" 351 if mir_op: 352 mir_name = name if mir_op is True else mir_op 353 mir_accessor = f"M{mir_name}* mir() const {{ return mir_->to{mir_name}(); }};" 354 355 extra_name_decl = "" 356 if extra_name: 357 extra_name_decl = "inline const char* extraName() const;" 358 359 # Can be moved into the f-string when we use Python 3.12, see PEP 701. 360 def nl(ws): 361 return "\n" + ws 362 363 code = f""" 364 class {class_name} : public {parent_class}<{num_defs}, {num_operands}, {num_temps_total}> {{ 365 {nl(" ").join(args_members)} 366 367 public: 368 LIR_HEADER({name}) 369 370 {nl(" ").join(oper_indices)} 371 372 explicit {class_name}({", ".join(succ_params + oper_params + temp_params + args_params)}) : {parent_class}(classOpcode){", ".join([""] + args_initializers)} {{ 373 {constructor_init} 374 {nl(" ").join(succ_initializers)} 375 {nl(" ").join(oper_initializers)} 376 {nl(" ").join(temp_initializers)} 377 }} 378 379 {nl(" ").join(succ_getters)} 380 {nl(" ").join(oper_getters)} 381 {nl(" ").join(oper_setters)} 382 {nl(" ").join(temp_getters)} 383 {nl(" ").join(temp_setters)} 384 {nl(" ").join(args_getters)} 385 {mir_accessor} 386 {extra_name_decl} 387 }}; 388 """ 389 390 # Remove blank lines and add backslashes at line endings. 391 return "\\\n".join(line for line in code.splitlines() if line.strip()) 392 393 394 def mir_type_to_lir_type(mir_type): 395 assert mir_type and mir_type != "None" 396 397 if mir_type == "Value": 398 return "BoxedValue" 399 400 if mir_type == "Int64": 401 return "Int64" 402 403 return "WordSized" 404 405 406 def generate_lir_header(c_out, yaml_path, mir_yaml_path): 407 data = load_yaml(yaml_path) 408 409 # LIR_OPCODE_LIST opcode. 410 ops = [] 411 412 # Generated LIR op class definitions. 413 lir_op_classes = [] 414 415 for op in data: 416 name = op["name"] 417 418 gen_boilerplate = op.get("gen_boilerplate", True) 419 assert isinstance(gen_boilerplate, bool) 420 421 if gen_boilerplate: 422 result_type = op.get("result_type", None) 423 assert result_type is None or result_type in result_types 424 425 successors = op.get("successors", None) 426 assert successors is None or isinstance(successors, list) 427 428 operands = op.get("operands") or {} 429 assert isinstance(operands, dict) 430 431 arguments = op.get("arguments") or {} 432 assert isinstance(arguments, dict) 433 434 num_temps = op.get("num_temps", 0) 435 assert isinstance(num_temps, int) 436 437 num_temps64 = op.get("num_temps64", 0) 438 assert isinstance(num_temps64, int) 439 440 gen_boilerplate = op.get("gen_boilerplate", True) 441 assert isinstance(gen_boilerplate, bool) 442 443 call_instruction = op.get("call_instruction", None) 444 assert isinstance(call_instruction, (type(None), bool)) 445 446 mir_op = op.get("mir_op", None) 447 assert mir_op in (None, True) or isinstance(mir_op, str) 448 449 extra_name = op.get("extra_name", False) 450 assert isinstance(extra_name, bool) 451 452 defer_init = op.get("defer_init", False) 453 assert isinstance(defer_init, bool) 454 455 lir_op_classes.append( 456 gen_lir_class( 457 name, 458 result_type, 459 successors, 460 operands, 461 arguments, 462 num_temps, 463 num_temps64, 464 call_instruction, 465 mir_op, 466 extra_name, 467 defer_init, 468 ) 469 ) 470 471 ops.append(f"_({name})") 472 473 # Generate LIR instructions for MIR instructions with 'generate_lir': true 474 mir_data = load_yaml(mir_yaml_path) 475 476 for op in mir_data: 477 name = op["name"] 478 479 generate_lir = op.get("generate_lir", False) 480 assert isinstance(generate_lir, bool) 481 482 if generate_lir: 483 # If the instruction has a `lir_result_type`, use it. If not, derive 484 # the LIR result type from the MIR `result_type`. 485 lir_result_type = op.get("lir_result_type", None) 486 assert isinstance(lir_result_type, (type(None), str)) 487 488 result_type = op.get("result_type", None) 489 assert isinstance(result_type, (type(None), str)) 490 491 if lir_result_type is not None: 492 if lir_result_type == "none": 493 lir_result_type = None 494 else: 495 assert lir_result_type in result_types 496 elif result_type and result_type != "None": 497 lir_result_type = mir_type_to_lir_type(result_type) 498 assert lir_result_type in result_types 499 500 successors = None 501 502 operands_raw = op.get("operands", {}) 503 assert isinstance(operands_raw, dict) 504 505 operands = {op: mir_type_to_lir_type(ty) for op, ty in operands_raw.items()} 506 507 arguments = {} 508 509 num_temps = op.get("lir_temps", 0) 510 assert isinstance(num_temps, int) 511 512 num_temps64 = op.get("lir_temps64", 0) 513 assert isinstance(num_temps64, int) 514 515 call_instruction = op.get("possibly_calls", None) 516 assert isinstance(call_instruction, (type(None), bool)) 517 518 mir_op = True 519 520 extra_name = False 521 522 defer_init = False 523 524 lir_op_classes.append( 525 gen_lir_class( 526 name, 527 lir_result_type, 528 successors, 529 operands, 530 arguments, 531 num_temps, 532 num_temps64, 533 call_instruction, 534 mir_op, 535 extra_name, 536 defer_init, 537 ) 538 ) 539 540 ops.append(f"_({name})") 541 542 contents = "#define LIR_OPCODE_LIST(_)\\\n" 543 contents += "\\\n".join(ops) 544 contents += "\n\n" 545 546 contents += "#define LIR_OPCODE_CLASS_GENERATED \\\n" 547 contents += "\\\n".join(lir_op_classes) 548 contents += "\n\n" 549 550 generate_header(c_out, "jit_LIROpsGenerated_h", contents)