GenerateMIRFiles.py (14262B)
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/MIROpsGenerated.h (list of MIR instructions) 6 # from MIROps.yaml, as well as MIR op definitions. 7 8 import io 9 10 import buildconfig 11 import yaml 12 from mozbuild.preprocessor import Preprocessor 13 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/. */ 18 19 #ifndef %(includeguard)s 20 #define %(includeguard)s 21 22 /* This file is generated by jit/GenerateMIRFiles.py. Do not edit! */ 23 24 %(contents)s 25 26 #endif // %(includeguard)s 27 """ 28 29 30 def generate_header(c_out, includeguard, contents): 31 c_out.write( 32 HEADER_TEMPLATE 33 % { 34 "includeguard": includeguard, 35 "contents": contents, 36 } 37 ) 38 39 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 = io.StringIO() 46 pp.do_filter("substitution") 47 pp.do_include(yaml_path) 48 contents = pp.out.getvalue() 49 return yaml.safe_load(contents) 50 51 52 type_policies = { 53 "Object": "ObjectPolicy", 54 "Value": "BoxPolicy", 55 "Int32": "UnboxedInt32Policy", 56 "BigInt": "BigIntPolicy", 57 "IntPtr": "IntPtrPolicy", 58 "Boolean": "BooleanPolicy", 59 "Double": "DoublePolicy", 60 "String": "StringPolicy", 61 "Symbol": "SymbolPolicy", 62 "NoTypePolicy": "NoTypePolicy", 63 "Slots": "NoTypePolicy", 64 } 65 66 67 def decide_type_policy(types, no_type_policy): 68 if no_type_policy: 69 return "public NoTypePolicy::Data" 70 71 type_num = 0 72 mixed_type_policies = [] 73 for mir_type in types: 74 policy = type_policies[mir_type] 75 if policy == "NoTypePolicy": 76 type_num += 1 77 continue 78 mixed_type_policies.append(f"{policy}<{type_num}>") 79 type_num += 1 80 81 if len(mixed_type_policies) == 0: 82 return "public NoTypePolicy::Data" 83 84 if len(mixed_type_policies) == 1: 85 return f"public {mixed_type_policies[0]}::Data" 86 87 return "public MixPolicy<{}>::Data".format(", ".join(mixed_type_policies)) 88 89 90 mir_base_class = [ 91 "MNullaryInstruction", 92 "MUnaryInstruction", 93 "MBinaryInstruction", 94 "MTernaryInstruction", 95 "MQuaternaryInstruction", 96 "MQuinaryInstruction", 97 ] 98 99 100 gc_pointer_types = [ 101 "JSObject*", 102 "NativeObject*", 103 "JSFunction*", 104 "BaseScript*", 105 "PropertyName*", 106 "Shape*", 107 "GetterSetter*", 108 "JSOffThreadAtom*", 109 "ClassBodyScope*", 110 "VarScope*", 111 "NamedLambdaObject*", 112 "RegExpObject*", 113 "JSScript*", 114 "LexicalScope*", 115 ] 116 117 special_storage_types = { 118 "JSOffThreadAtom*": ( 119 "CompilerGCPointer<JSAtom*>", 120 "{}->unwrap()", 121 "&{}->asOffThreadAtom()", 122 ) 123 } 124 125 126 def arg_type_sig_to_init(type_sig, arg_name): 127 if type_sig in special_types: 128 _, init, _ = special_types[type_sig] 129 return init.format(arg_name) 130 else: 131 return arg_name 132 133 134 def gen_mir_class( 135 name, 136 operands, 137 arguments, 138 no_type_policy, 139 result, 140 guard, 141 movable, 142 folds_to, 143 value_hash, 144 congruent_to, 145 alias_set, 146 might_alias, 147 possibly_calls, 148 compute_range, 149 can_recover, 150 clone, 151 can_consume_float32, 152 ): 153 """Generates class definition for a single MIR opcode.""" 154 155 # Generate a MIR opcode class definition. 156 # For example: 157 # class MGuardIndexIsValidUpdateOrAdd 158 # : public MBinaryInstruction, 159 # public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>>::Data { 160 # explicit MGuardIndexIsValidUpdateOrAdd(MDefinition* object, 161 # MDefinition* index) 162 # : MBinaryInstruction(classOpcode, object, index) { 163 # setGuard(); 164 # setMovable(); 165 # setResultType(MIRType::Int32); 166 # } 167 # public: 168 # INSTRUCTION_HEADER(GetFrameArgument) 169 # TRIVIAL_NEW_WRAPPERS 170 # NAMED_OPERANDS((0, object), (1, index)) 171 # AliasSet getAliasSet() const override { return AliasSet::None(); } 172 # bool congruentTo(const MDefinition* ins) const override { 173 # return congruentIfOperandsEqual(ins); } 174 # }; 175 # 176 177 type_policy = "" 178 # MIR op constructor operands. 179 mir_operands = [] 180 # MIR op base class constructor operands. 181 mir_base_class_operands = [] 182 # Types of each constructor operand. 183 mir_types = [] 184 # Items for NAMED_OPERANDS. 185 named_operands = [] 186 if operands: 187 current_oper_num = 0 188 for oper_name in operands: 189 oper = "MDefinition* " + oper_name 190 mir_operands.append(oper) 191 mir_base_class_operands.append(", " + oper_name) 192 # Collect all the MIR argument types to use for determining the 193 # ops type policy. 194 mir_types.append(operands[oper_name]) 195 # Collecting named operands for defining accessors. 196 named_operands.append(f"({current_oper_num}, {oper_name})") 197 current_oper_num += 1 198 type_policy = decide_type_policy(mir_types, no_type_policy) 199 200 class_name = "M" + name 201 202 assert len(mir_operands) < 6 203 base_class = mir_base_class[len(mir_operands)] 204 assert base_class 205 if base_class != "MNullaryInstruction": 206 assert type_policy 207 type_policy = ", " + type_policy 208 code = f"class {class_name} : public {base_class}{type_policy} {{\\\n" 209 210 # Arguments to class constructor that require accessors. 211 mir_args = [] 212 if arguments: 213 for arg_name in arguments: 214 arg_type_sig = arguments[arg_name] 215 mir_args.append(arg_type_sig + " " + arg_name) 216 if arg_type_sig in special_storage_types: 217 storage, _, _ = special_storage_types[arg_type_sig] 218 code += " " + storage 219 elif arg_type_sig in gc_pointer_types: 220 code += " CompilerGCPointer<" + arg_type_sig + ">" 221 else: 222 code += " " + arg_type_sig 223 code += " " + arg_name + "_;\\\n" 224 225 code += " explicit {}({}) : {}(classOpcode{})".format( 226 class_name, 227 ", ".join(mir_operands + mir_args), 228 base_class, 229 "".join(mir_base_class_operands), 230 ) 231 if arguments: 232 for arg_name in arguments: 233 code += ", " + arg_name + "_(" 234 arg_type_sig = arguments[arg_name] 235 if arg_type_sig in special_storage_types: 236 _, init, _ = special_storage_types[arg_type_sig] 237 code += init.format(arg_name) 238 else: 239 code += arg_name 240 code += ")" 241 code += " {\\\n" 242 if guard: 243 code += " setGuard();\\\n" 244 if movable: 245 code += " setMovable();\\\n" 246 # Note: MIRType::None is the default MIR result type so don't generate a 247 # setResultType call for it. 248 if result and result != "None": 249 code += f" setResultType(MIRType::{result});\\\n" 250 code += " }\\\n public:\\\n" 251 if arguments: 252 for arg_name in arguments: 253 code += " " + arguments[arg_name] + " " + arg_name + "() const { " 254 arg_type_sig = arguments[arg_name] 255 if arg_type_sig in special_storage_types: 256 _, _, load = special_storage_types[arg_type_sig] 257 code += "return " + load.format(arg_name + "_") + "; }\\\n" 258 else: 259 code += "return " + arg_name + "_; }\\\n" 260 code += f" INSTRUCTION_HEADER({name})\\\n" 261 code += " TRIVIAL_NEW_WRAPPERS\\\n" 262 if named_operands: 263 code += " NAMED_OPERANDS({})\\\n".format(", ".join(named_operands)) 264 if alias_set: 265 if alias_set == "custom": 266 code += " AliasSet getAliasSet() const override;\\\n" 267 else: 268 assert alias_set == "none" 269 code += ( 270 " AliasSet getAliasSet() const override { " 271 "return AliasSet::None(); }\\\n" 272 ) 273 if might_alias: 274 code += " AliasType mightAlias(const MDefinition* store) const override;\\\n" 275 if folds_to: 276 code += " MDefinition* foldsTo(TempAllocator& alloc) override;\\\n" 277 if congruent_to: 278 if congruent_to == "custom": 279 code += " bool congruentTo(const MDefinition* ins) const override;\\\n" 280 else: 281 assert congruent_to == "if_operands_equal" 282 code += ( 283 " bool congruentTo(const MDefinition* ins) const override { " 284 "return congruentIfOperandsEqual(ins); }\\\n" 285 ) 286 if value_hash: 287 assert value_hash == "custom" 288 code += " HashNumber valueHash() const override;\\\n" 289 if possibly_calls: 290 if possibly_calls == "custom": 291 code += " bool possiblyCalls() const override;\\\n" 292 else: 293 code += " bool possiblyCalls() const override { return true; }\\\n" 294 if compute_range: 295 code += " void computeRange(TempAllocator& alloc) override;\\\n" 296 if can_recover: 297 code += " [[nodiscard]] bool writeRecoverData(\\\n" 298 code += " CompactBufferWriter& writer) const override;\\\n" 299 if can_recover == "custom": 300 code += " bool canRecoverOnBailout() const override;\\\n" 301 else: 302 code += " bool canRecoverOnBailout() const override { return true; }\\\n" 303 if clone: 304 code += " ALLOW_CLONE(" + class_name + ")\\\n" 305 if can_consume_float32: 306 code += ( 307 " bool canConsumeFloat32(MUse* use) const override { return true; }\\\n" 308 ) 309 code += "};\\\n" 310 return code 311 312 313 def gen_non_gc_pointer_type_assertions(seen_types): 314 """Generates a list of static assertions used to ensure that all argument 315 types seen are not derived from gc::Cell, ensuring that gc pointer arguments 316 are added to the gc_pointer_types list. 317 """ 318 assertions = [] 319 320 for seen_type in sorted(seen_types): 321 assertions.append( 322 "static_assert(!std::is_base_of_v<gc::Cell, " + seen_type.strip("*") + ">, " 323 '"Ensure that ' 324 + seen_type.strip("*") 325 + ' is added to the gc_pointer_types list in GenerateMIRFiles.py."' 326 ");" 327 ) 328 329 return assertions 330 331 332 def generate_mir_header(c_out, yaml_path): 333 """Generate MIROpsGenerated.h from MIROps.yaml. The generated file 334 has a list of MIR ops and boilerplate for MIR op definitions. 335 """ 336 337 data = load_yaml(yaml_path) 338 339 # MIR_OPCODE_LIST items. Stores the name of each MIR op. 340 ops_items = [] 341 342 # Generated MIR op class definitions. 343 mir_op_classes = [] 344 345 # Unique and non gc pointer types seen for arguments to the MIR constructor. 346 seen_non_gc_pointer_argument_types = set() 347 348 for op in data: 349 name = op["name"] 350 351 ops_items.append(f"_({name})") 352 353 gen_boilerplate = op.get("gen_boilerplate", True) 354 assert isinstance(gen_boilerplate, bool) 355 356 if gen_boilerplate: 357 operands = op.get("operands", None) 358 assert operands is None or isinstance(operands, dict) 359 360 arguments = op.get("arguments", None) 361 assert arguments is None or isinstance(arguments, dict) 362 363 no_type_policy = op.get("type_policy", None) 364 assert no_type_policy in (None, "none") 365 366 result = op.get("result_type", None) 367 assert result is None or isinstance(result, str) 368 369 guard = op.get("guard", None) 370 assert guard in (None, True, False) 371 372 movable = op.get("movable", None) 373 assert movable in (None, True, False) 374 375 folds_to = op.get("folds_to", None) 376 assert folds_to in (None, "custom") 377 378 value_hash = op.get("value_hash", None) 379 assert value_hash in (None, "custom") 380 381 congruent_to = op.get("congruent_to", None) 382 assert congruent_to in (None, "if_operands_equal", "custom") 383 384 alias_set = op.get("alias_set", None) 385 assert alias_set in (None, "none", "custom") 386 387 might_alias = op.get("might_alias", None) 388 assert might_alias in (None, "custom") 389 390 possibly_calls = op.get("possibly_calls", None) 391 assert possibly_calls in (None, True, "custom") 392 393 compute_range = op.get("compute_range", None) 394 assert compute_range in (None, "custom") 395 396 can_recover = op.get("can_recover", None) 397 assert can_recover in (None, True, False, "custom") 398 399 clone = op.get("clone", None) 400 assert clone in (None, True, False) 401 402 can_consume_float32 = op.get("can_consume_float32", None) 403 assert can_consume_float32 in (None, True, False) 404 405 code = gen_mir_class( 406 name, 407 operands, 408 arguments, 409 no_type_policy, 410 result, 411 guard, 412 movable, 413 folds_to, 414 value_hash, 415 congruent_to, 416 alias_set, 417 might_alias, 418 possibly_calls, 419 compute_range, 420 can_recover, 421 clone, 422 can_consume_float32, 423 ) 424 mir_op_classes.append(code) 425 426 if arguments: 427 for argument in arguments: 428 arg_type = arguments[argument] 429 if arg_type not in gc_pointer_types: 430 seen_non_gc_pointer_argument_types.add(arg_type) 431 432 contents = "#define MIR_OPCODE_LIST(_)\\\n" 433 contents += "\\\n".join(ops_items) 434 contents += "\n\n" 435 436 contents += "#define MIR_OPCODE_CLASS_GENERATED \\\n" 437 contents += "\\\n".join(mir_op_classes) 438 contents += "\n\n" 439 440 contents += "#define NON_GC_POINTER_TYPE_ASSERTIONS_GENERATED \\\n" 441 contents += "\\\n".join( 442 gen_non_gc_pointer_type_assertions(seen_non_gc_pointer_argument_types) 443 ) 444 contents += "\n\n" 445 446 generate_header(c_out, "jit_MIROpsGenerated_h", contents)