|
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.",
|
@@ -1218,7 +1229,10 @@ def write_metadata(self) -> None:
|
1218 | 1229 | self.out.emit("struct { int16_t uop; int8_t size; int8_t offset; } uops[8];")
|
1219 | 1230 | self.out.emit("")
|
1220 | 1231 |
|
| 1232 | + for key, value in OPARG_SIZES.items(): |
| 1233 | + self.out.emit(f"#define {key} {value}") |
1221 | 1234 | self.out.emit("")
|
| 1235 | + |
1222 | 1236 | self.out.emit("#define OPCODE_METADATA_FMT(OP) "
|
1223 | 1237 | "(_PyOpcode_opcode_metadata[(OP)].instr_format)")
|
1224 | 1238 | self.out.emit("#define SAME_OPCODE_METADATA(OP1, OP2) \\")
|
@@ -1268,6 +1282,9 @@ def write_metadata(self) -> None:
|
1268 | 1282 | # Construct a dummy Component -- input/output mappings are not used
|
1269 | 1283 | part = Component(instr, [], [], instr.active_caches)
|
1270 | 1284 | self.write_macro_expansions(instr.name, [part])
|
| 1285 | + elif instr.kind == "inst" and variable_used(instr.inst, "oparg1"): |
| 1286 | + assert variable_used(instr.inst, "oparg2"), "Half super-instr?" |
| 1287 | + self.write_super_expansions(instr.name) |
1271 | 1288 | case parser.Macro():
|
1272 | 1289 | mac = self.macro_instrs[thing.name]
|
1273 | 1290 | self.write_macro_expansions(mac.name, mac.parts)
|
@@ -1348,18 +1365,58 @@ def write_macro_expansions(self, name: str, parts: MacroParts) -> None:
|
1348 | 1365 | print(f"NOTE: Part {part.instr.name} of {name} is not a viable uop")
|
1349 | 1366 | return
|
1350 | 1367 | if part.instr.instr_flags.HAS_ARG_FLAG or not part.active_caches:
|
1351 |
| - size, offset = 0, 0 |
| 1368 | + size, offset = OPARG_SIZES["OPARG_FULL"], 0 |
1352 | 1369 | else:
|
1353 | 1370 | # If this assert triggers, is_viable_uops() lied
|
1354 | 1371 | assert len(part.active_caches) == 1, (name, part.instr.name)
|
1355 | 1372 | cache = part.active_caches[0]
|
1356 | 1373 | size, offset = cache.effect.size, cache.offset
|
1357 | 1374 | expansions.append((part.instr.name, size, offset))
|
1358 | 1375 | assert len(expansions) > 0, f"Macro {name} has empty expansion?!"
|
| 1376 | + self.write_expansions(name, expansions) |
| 1377 | + |
| 1378 | + def write_super_expansions(self, name: str) -> None: |
| 1379 | + """Write special macro expansions for super-instructions. |
| 1380 | +
|
| 1381 | + If you get an assertion failure here, you probably have accidentally |
| 1382 | + violated one of the assumptions here. |
| 1383 | +
|
| 1384 | + - A super-instruction's name is of the form FIRST_SECOND where |
| 1385 | + FIRST and SECOND are regular instructions whose name has the |
| 1386 | + form FOO_BAR. Thus, there must be exactly 3 underscores. |
| 1387 | + Example: LOAD_CONST_STORE_FAST. |
| 1388 | +
|
| 1389 | + - A super-instruction's body uses `oparg1 and `oparg2`, and no |
| 1390 | + other instruction's body uses those variable names. |
| 1391 | +
|
| 1392 | + - A super-instruction has no active (used) cache entries. |
| 1393 | +
|
| 1394 | + In the expansion, the first instruction's operand is all but the |
| 1395 | + bottom 4 bits of the super-instruction's oparg, and the second |
| 1396 | + instruction's operand is the bottom 4 bits. We use the special |
| 1397 | + size codes OPARG_TOP and OPARG_BOTTOM for these. |
| 1398 | + """ |
| 1399 | + pieces = name.split("_") |
| 1400 | + assert len(pieces) == 4, f"{name} doesn't look like a super-instr" |
| 1401 | + name1 = "_".join(pieces[:2]) |
| 1402 | + name2 = "_".join(pieces[2:]) |
| 1403 | + assert name1 in self.instrs, f"{name1} doesn't match any instr" |
| 1404 | + assert name2 in self.instrs, f"{name2} doesn't match any instr" |
| 1405 | + instr1 = self.instrs[name1] |
| 1406 | + instr2 = self.instrs[name2] |
| 1407 | + assert not instr1.active_caches, f"{name1} has active caches" |
| 1408 | + assert not instr2.active_caches, f"{name2} has active caches" |
| 1409 | + expansions = [ |
| 1410 | + (name1, OPARG_SIZES["OPARG_TOP"], 0), |
| 1411 | + (name2, OPARG_SIZES["OPARG_BOTTOM"], 0), |
| 1412 | + ] |
| 1413 | + self.write_expansions(name, expansions) |
| 1414 | + |
| 1415 | + def write_expansions(self, name: str, expansions: list[tuple[str, int, int]]) -> None: |
1359 | 1416 | pieces = [f"{{ {name}, {size}, {offset} }}" for name, size, offset in expansions]
|
1360 | 1417 | self.out.emit(
|
1361 | 1418 | f"[{name}] = "
|
1362 |
| - f"{{ .nuops = {len(expansions)}, .uops = {{ {', '.join(pieces)} }} }}," |
| 1419 | + f"{{ .nuops = {len(pieces)}, .uops = {{ {', '.join(pieces)} }} }}," |
1363 | 1420 | )
|
1364 | 1421 |
|
1365 | 1422 | def emit_metadata_entry(
|
|
0 commit comments