|
40 | 40 | UNUSED = "unused"
|
41 | 41 | BITS_PER_CODE_UNIT = 16
|
42 | 42 |
|
| 43 | +# Constants used instead of size for macro expansions. |
| 44 | +# Note: 1, 2, 4 must match actual cache entry sizes. |
| 45 | +OPARG_SIZES = { |
| 46 | + "OPARG_FULL": 0, |
| 47 | + "OPARG_CACHE_1": 1, |
| 48 | + "OPARG_CACHE_2": 2, |
| 49 | + "OPARG_CACHE_4": 4, |
| 50 | + "OPARG_TOP": 5, |
| 51 | + "OPARG_BOTTOM": 6, |
| 52 | +} |
| 53 | + |
43 | 54 | RESERVED_WORDS = {
|
44 | 55 | "co_consts" : "Use FRAME_CO_CONSTS.",
|
45 | 56 | "co_names": "Use FRAME_CO_NAMES.",
|
@@ -1213,7 +1224,10 @@ def write_metadata(self) -> None:
|
1213 | 1224 | self.out.emit("struct { int16_t uop; int8_t size; int8_t offset; } uops[8];")
|
1214 | 1225 | self.out.emit("")
|
1215 | 1226 |
|
| 1227 | + for key, value in OPARG_SIZES.items(): |
| 1228 | + self.out.emit(f"#define {key} {value}") |
1216 | 1229 | self.out.emit("")
|
| 1230 | + |
1217 | 1231 | self.out.emit("#define OPCODE_METADATA_FMT(OP) "
|
1218 | 1232 | "(_PyOpcode_opcode_metadata[(OP)].instr_format)")
|
1219 | 1233 | self.out.emit("#define SAME_OPCODE_METADATA(OP1, OP2) \\")
|
@@ -1263,6 +1277,9 @@ def write_metadata(self) -> None:
|
1263 | 1277 | # Construct a dummy Component -- input/output mappings are not used
|
1264 | 1278 | part = Component(instr, [], [], instr.active_caches)
|
1265 | 1279 | self.write_macro_expansions(instr.name, [part])
|
| 1280 | + elif instr.kind == "inst" and variable_used(instr.inst, "oparg1"): |
| 1281 | + assert variable_used(instr.inst, "oparg2"), "Half super-instr?" |
| 1282 | + self.write_super_expansions(instr.name) |
1266 | 1283 | case parser.Macro():
|
1267 | 1284 | mac = self.macro_instrs[thing.name]
|
1268 | 1285 | self.write_macro_expansions(mac.name, mac.parts)
|
@@ -1342,18 +1359,58 @@ def write_macro_expansions(self, name: str, parts: MacroParts) -> None:
|
1342 | 1359 | print(f"NOTE: Part {part.instr.name} of {name} is not a viable uop")
|
1343 | 1360 | return
|
1344 | 1361 | if part.instr.instr_flags.HAS_ARG_FLAG or not part.active_caches:
|
1345 |
| - size, offset = 0, 0 |
| 1362 | + size, offset = OPARG_SIZES["OPARG_FULL"], 0 |
1346 | 1363 | else:
|
1347 | 1364 | # If this assert triggers, is_viable_uops() lied
|
1348 | 1365 | assert len(part.active_caches) == 1, (name, part.instr.name)
|
1349 | 1366 | cache = part.active_caches[0]
|
1350 | 1367 | size, offset = cache.effect.size, cache.offset
|
1351 | 1368 | expansions.append((part.instr.name, size, offset))
|
1352 | 1369 | assert len(expansions) > 0, f"Macro {name} has empty expansion?!"
|
| 1370 | + self.write_expansions(name, expansions) |
| 1371 | + |
| 1372 | + def write_super_expansions(self, name: str) -> None: |
| 1373 | + """Write special macro expansions for super-instructions. |
| 1374 | +
|
| 1375 | + If you get an assertion failure here, you probably have accidentally |
| 1376 | + violated one of the assumptions here. |
| 1377 | +
|
| 1378 | + - A super-instruction's name is of the form FIRST_SECOND where |
| 1379 | + FIRST and SECOND are regular instructions whose name has the |
| 1380 | + form FOO_BAR. Thus, there must be exactly 3 underscores. |
| 1381 | + Example: LOAD_CONST_STORE_FAST. |
| 1382 | +
|
| 1383 | + - A super-instruction's body uses `oparg1 and `oparg2`, and no |
| 1384 | + other instruction's body uses those variable names. |
| 1385 | +
|
| 1386 | + - A super-instruction has no active (used) cache entries. |
| 1387 | +
|
| 1388 | + In the expansion, the first instruction's operand is all but the |
| 1389 | + bottom 4 bits of the super-instruction's oparg, and the second |
| 1390 | + instruction's operand is the bottom 4 bits. We use the special |
| 1391 | + size codes OPARG_TOP and OPARG_BOTTOM for these. |
| 1392 | + """ |
| 1393 | + pieces = name.split("_") |
| 1394 | + assert len(pieces) == 4, f"{name} doesn't look like a super-instr" |
| 1395 | + name1 = "_".join(pieces[:2]) |
| 1396 | + name2 = "_".join(pieces[2:]) |
| 1397 | + assert name1 in self.instrs, f"{name1} doesn't match any instr" |
| 1398 | + assert name2 in self.instrs, f"{name2} doesn't match any instr" |
| 1399 | + instr1 = self.instrs[name1] |
| 1400 | + instr2 = self.instrs[name2] |
| 1401 | + assert not instr1.active_caches, f"{name1} has active caches" |
| 1402 | + assert not instr2.active_caches, f"{name2} has active caches" |
| 1403 | + expansions = [ |
| 1404 | + (name1, OPARG_SIZES["OPARG_TOP"], 0), |
| 1405 | + (name2, OPARG_SIZES["OPARG_BOTTOM"], 0), |
| 1406 | + ] |
| 1407 | + self.write_expansions(name, expansions) |
| 1408 | + |
| 1409 | + def write_expansions(self, name: str, expansions: list[tuple[str, int, int]]) -> None: |
1353 | 1410 | pieces = [f"{{ {name}, {size}, {offset} }}" for name, size, offset in expansions]
|
1354 | 1411 | self.out.emit(
|
1355 | 1412 | f"[{name}] = "
|
1356 |
| - f"{{ .nuops = {len(expansions)}, .uops = {{ {', '.join(pieces)} }} }}," |
| 1413 | + f"{{ .nuops = {len(pieces)}, .uops = {{ {', '.join(pieces)} }} }}," |
1357 | 1414 | )
|
1358 | 1415 |
|
1359 | 1416 | def emit_metadata_entry(
|
|
0 commit comments