2024-06-08 18:08:21 +00:00
|
|
|
# mypy: allow-untyped-defs
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
import bisect
|
2022-10-13 23:18:06 +00:00
|
|
|
import dataclasses
|
|
|
|
|
import dis
|
|
|
|
|
import sys
|
2025-01-18 16:56:06 +00:00
|
|
|
from typing import Any, Union
|
2022-10-13 23:18:06 +00:00
|
|
|
|
2024-07-31 13:58:25 +00:00
|
|
|
|
2022-10-13 23:18:06 +00:00
|
|
|
TERMINAL_OPCODES = {
|
|
|
|
|
dis.opmap["RETURN_VALUE"],
|
|
|
|
|
dis.opmap["JUMP_FORWARD"],
|
|
|
|
|
dis.opmap["RAISE_VARARGS"],
|
|
|
|
|
# TODO(jansel): double check exception handling
|
|
|
|
|
}
|
2025-01-18 16:56:06 +00:00
|
|
|
TERMINAL_OPCODES.add(dis.opmap["RERAISE"])
|
2023-02-02 19:45:45 +00:00
|
|
|
if sys.version_info >= (3, 11):
|
|
|
|
|
TERMINAL_OPCODES.add(dis.opmap["JUMP_BACKWARD"])
|
2023-03-30 22:59:05 +00:00
|
|
|
TERMINAL_OPCODES.add(dis.opmap["JUMP_FORWARD"])
|
2023-02-02 19:45:45 +00:00
|
|
|
else:
|
|
|
|
|
TERMINAL_OPCODES.add(dis.opmap["JUMP_ABSOLUTE"])
|
2024-03-26 22:52:48 +00:00
|
|
|
if sys.version_info >= (3, 12):
|
|
|
|
|
TERMINAL_OPCODES.add(dis.opmap["RETURN_CONST"])
|
[dynamo, 3.13] add JUMP_BACKWARD_NO_INTERRUPT to terminal opcodes (#141859)
Pull Request resolved: https://github.com/pytorch/pytorch/pull/141859
Approved by: https://github.com/StrongerXi, https://github.com/atalman
ghstack dependencies: #141409, #142003, #141572, #141577, #141605, #141621, #141623, #141673, #141674, #141858, #141862, #139533, #140733
2024-12-04 08:02:51 +00:00
|
|
|
if sys.version_info >= (3, 13):
|
|
|
|
|
TERMINAL_OPCODES.add(dis.opmap["JUMP_BACKWARD_NO_INTERRUPT"])
|
2022-10-13 23:18:06 +00:00
|
|
|
JUMP_OPCODES = set(dis.hasjrel + dis.hasjabs)
|
2023-02-14 00:43:41 +00:00
|
|
|
JUMP_OPNAMES = {dis.opname[opcode] for opcode in JUMP_OPCODES}
|
2022-10-13 23:18:06 +00:00
|
|
|
HASLOCAL = set(dis.haslocal)
|
|
|
|
|
HASFREE = set(dis.hasfree)
|
|
|
|
|
|
2023-01-29 18:28:46 +00:00
|
|
|
stack_effect = dis.stack_effect
|
2022-10-13 23:18:06 +00:00
|
|
|
|
|
|
|
|
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
def get_indexof(insts):
|
|
|
|
|
"""
|
|
|
|
|
Get a mapping from instruction memory address to index in instruction list.
|
|
|
|
|
Additionally checks that each instruction only appears once in the list.
|
|
|
|
|
"""
|
|
|
|
|
indexof = {}
|
|
|
|
|
for i, inst in enumerate(insts):
|
|
|
|
|
assert inst not in indexof
|
|
|
|
|
indexof[inst] = i
|
|
|
|
|
return indexof
|
|
|
|
|
|
|
|
|
|
|
2022-10-13 23:18:06 +00:00
|
|
|
def remove_dead_code(instructions):
|
|
|
|
|
"""Dead code elimination"""
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
indexof = get_indexof(instructions)
|
2022-10-13 23:18:06 +00:00
|
|
|
live_code = set()
|
|
|
|
|
|
|
|
|
|
def find_live_code(start):
|
|
|
|
|
for i in range(start, len(instructions)):
|
|
|
|
|
if i in live_code:
|
|
|
|
|
return
|
|
|
|
|
live_code.add(i)
|
|
|
|
|
inst = instructions[i]
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
if inst.exn_tab_entry:
|
|
|
|
|
find_live_code(indexof[inst.exn_tab_entry.target])
|
2022-10-13 23:18:06 +00:00
|
|
|
if inst.opcode in JUMP_OPCODES:
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
find_live_code(indexof[inst.target])
|
2022-10-13 23:18:06 +00:00
|
|
|
if inst.opcode in TERMINAL_OPCODES:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
find_live_code(0)
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
|
|
|
|
|
# change exception table entries if start/end instructions are dead
|
|
|
|
|
# assumes that exception table entries have been propagated,
|
|
|
|
|
# e.g. with bytecode_transformation.propagate_inst_exn_table_entries,
|
|
|
|
|
# and that instructions with an exn_tab_entry lies within its start/end.
|
|
|
|
|
if sys.version_info >= (3, 11):
|
|
|
|
|
live_idx = sorted(live_code)
|
|
|
|
|
for i, inst in enumerate(instructions):
|
|
|
|
|
if i in live_code and inst.exn_tab_entry:
|
|
|
|
|
# find leftmost live instruction >= start
|
|
|
|
|
start_idx = bisect.bisect_left(
|
|
|
|
|
live_idx, indexof[inst.exn_tab_entry.start]
|
|
|
|
|
)
|
|
|
|
|
assert start_idx < len(live_idx)
|
|
|
|
|
# find rightmost live instruction <= end
|
|
|
|
|
end_idx = (
|
|
|
|
|
bisect.bisect_right(live_idx, indexof[inst.exn_tab_entry.end]) - 1
|
|
|
|
|
)
|
|
|
|
|
assert end_idx >= 0
|
|
|
|
|
assert live_idx[start_idx] <= i <= live_idx[end_idx]
|
|
|
|
|
inst.exn_tab_entry.start = instructions[live_idx[start_idx]]
|
|
|
|
|
inst.exn_tab_entry.end = instructions[live_idx[end_idx]]
|
|
|
|
|
|
2022-10-13 23:18:06 +00:00
|
|
|
return [inst for i, inst in enumerate(instructions) if i in live_code]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def remove_pointless_jumps(instructions):
|
|
|
|
|
"""Eliminate jumps to the next instruction"""
|
|
|
|
|
pointless_jumps = {
|
|
|
|
|
id(a)
|
|
|
|
|
for a, b in zip(instructions, instructions[1:])
|
|
|
|
|
if a.opname == "JUMP_ABSOLUTE" and a.target is b
|
|
|
|
|
}
|
|
|
|
|
return [inst for inst in instructions if id(inst) not in pointless_jumps]
|
|
|
|
|
|
|
|
|
|
|
2022-10-19 22:44:01 +00:00
|
|
|
def propagate_line_nums(instructions):
|
|
|
|
|
"""Ensure every instruction has line number set in case some are removed"""
|
|
|
|
|
cur_line_no = None
|
|
|
|
|
|
|
|
|
|
def populate_line_num(inst):
|
|
|
|
|
nonlocal cur_line_no
|
|
|
|
|
if inst.starts_line:
|
|
|
|
|
cur_line_no = inst.starts_line
|
|
|
|
|
|
|
|
|
|
inst.starts_line = cur_line_no
|
|
|
|
|
|
|
|
|
|
for inst in instructions:
|
|
|
|
|
populate_line_num(inst)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def remove_extra_line_nums(instructions):
|
|
|
|
|
"""Remove extra starts line properties before packing bytecode"""
|
|
|
|
|
|
|
|
|
|
cur_line_no = None
|
|
|
|
|
|
|
|
|
|
def remove_line_num(inst):
|
|
|
|
|
nonlocal cur_line_no
|
|
|
|
|
if inst.starts_line is None:
|
|
|
|
|
return
|
|
|
|
|
elif inst.starts_line == cur_line_no:
|
|
|
|
|
inst.starts_line = None
|
|
|
|
|
else:
|
|
|
|
|
cur_line_no = inst.starts_line
|
|
|
|
|
|
|
|
|
|
for inst in instructions:
|
|
|
|
|
remove_line_num(inst)
|
|
|
|
|
|
|
|
|
|
|
2022-10-13 23:18:06 +00:00
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class ReadsWrites:
|
2025-01-18 16:56:06 +00:00
|
|
|
reads: set[Any]
|
|
|
|
|
writes: set[Any]
|
|
|
|
|
visited: set[Any]
|
2022-10-13 23:18:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def livevars_analysis(instructions, instruction):
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
indexof = get_indexof(instructions)
|
2022-10-13 23:18:06 +00:00
|
|
|
must = ReadsWrites(set(), set(), set())
|
|
|
|
|
may = ReadsWrites(set(), set(), set())
|
|
|
|
|
|
|
|
|
|
def walk(state, start):
|
|
|
|
|
if start in state.visited:
|
|
|
|
|
return
|
|
|
|
|
state.visited.add(start)
|
|
|
|
|
|
|
|
|
|
for i in range(start, len(instructions)):
|
|
|
|
|
inst = instructions[i]
|
|
|
|
|
if inst.opcode in HASLOCAL or inst.opcode in HASFREE:
|
|
|
|
|
if "LOAD" in inst.opname or "DELETE" in inst.opname:
|
|
|
|
|
if inst.argval not in must.writes:
|
|
|
|
|
state.reads.add(inst.argval)
|
|
|
|
|
elif "STORE" in inst.opname:
|
|
|
|
|
state.writes.add(inst.argval)
|
2023-03-30 22:59:05 +00:00
|
|
|
elif inst.opname == "MAKE_CELL":
|
|
|
|
|
pass
|
2022-10-13 23:18:06 +00:00
|
|
|
else:
|
|
|
|
|
raise NotImplementedError(f"unhandled {inst.opname}")
|
2023-05-08 23:26:19 +00:00
|
|
|
if inst.exn_tab_entry:
|
|
|
|
|
walk(may, indexof[inst.exn_tab_entry.target])
|
2022-10-13 23:18:06 +00:00
|
|
|
if inst.opcode in JUMP_OPCODES:
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
walk(may, indexof[inst.target])
|
2022-10-13 23:18:06 +00:00
|
|
|
state = may
|
|
|
|
|
if inst.opcode in TERMINAL_OPCODES:
|
|
|
|
|
return
|
|
|
|
|
|
[dynamo 3.11] implement 3.11 exceptiontable (#96511)
Summary of changes:
- Add CPython exceptiontable parsing/assembling functions in torch/_dynamo/bytecode_transformation.py, based on https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt.
- Add optional `exn_tab_entry` field to dynamo `Instruction`s in torch/_dynamo/bytecode_transformation.py in order to virtualize exception table entries (start, end, target instructions).
- Add checks guarding against duplicate instructions in dynamo, so that jump/exceptiontable targets are unambiguous. See `get_indexof` in torch/_dynamo/bytecode_analysis.py. Ensure that bytecode generation throughout dynamo does not generate duplicate instructions.
- Allow dynamo bytecode generation logic to generate nested exception table entries for developer convenience. CPython expects entries to not overlap, so we flatten nested entries during assembly in torch/_dynamo/bytecode_transformation.py:compute_exception_table.
- Simulate the block stack in torch/_dynamo/symbolic_convert.py. CPython removed the block stack in 3.11, but dynamo needs it in order to keep track of active contexts. So we simulate the block stack as before by looking at exceptiontable entries in order to determine the current blocks.
- Update context codegen in torch/_dynamo/resume_execution.py. The `SETUP_FINALLY` bytecode, which conveniently had a jump target to the finally block, was removed in 3.11, so we need to keep track of the jump target of the finally block using exceptiontables. Generating resume functions is more difficult since the original exceptiontable entries pointing to old cleanup code need to be modified to point to new cleanup code.
- Fix a push_null bug in torch/_dynamo/variables/functions.py introduced by https://github.com/pytorch/pytorch/pull/98699
Pull Request resolved: https://github.com/pytorch/pytorch/pull/96511
Approved by: https://github.com/jansel, https://github.com/yanboliang, https://github.com/albanD
2023-04-17 22:48:39 +00:00
|
|
|
walk(must, indexof[instruction])
|
2022-10-13 23:18:06 +00:00
|
|
|
return must.reads | may.reads
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class FixedPointBox:
|
|
|
|
|
value: bool = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class StackSize:
|
2023-11-08 17:58:54 +00:00
|
|
|
low: Union[int, float]
|
|
|
|
|
high: Union[int, float]
|
2022-10-13 23:18:06 +00:00
|
|
|
fixed_point: FixedPointBox
|
|
|
|
|
|
|
|
|
|
def zero(self):
|
|
|
|
|
self.low = 0
|
|
|
|
|
self.high = 0
|
|
|
|
|
self.fixed_point.value = False
|
|
|
|
|
|
|
|
|
|
def offset_of(self, other, n):
|
|
|
|
|
prior = (self.low, self.high)
|
|
|
|
|
self.low = min(self.low, other.low + n)
|
|
|
|
|
self.high = max(self.high, other.high + n)
|
|
|
|
|
if (self.low, self.high) != prior:
|
|
|
|
|
self.fixed_point.value = False
|
|
|
|
|
|
2023-05-08 23:26:19 +00:00
|
|
|
def exn_tab_jump(self, depth):
|
|
|
|
|
prior = (self.low, self.high)
|
|
|
|
|
self.low = min(self.low, depth)
|
|
|
|
|
self.high = max(self.high, depth)
|
|
|
|
|
if (self.low, self.high) != prior:
|
|
|
|
|
self.fixed_point.value = False
|
|
|
|
|
|
2022-10-13 23:18:06 +00:00
|
|
|
|
2023-11-08 17:58:54 +00:00
|
|
|
def stacksize_analysis(instructions) -> Union[int, float]:
|
2022-10-13 23:18:06 +00:00
|
|
|
assert instructions
|
|
|
|
|
fixed_point = FixedPointBox()
|
|
|
|
|
stack_sizes = {
|
|
|
|
|
inst: StackSize(float("inf"), float("-inf"), fixed_point)
|
|
|
|
|
for inst in instructions
|
|
|
|
|
}
|
|
|
|
|
stack_sizes[instructions[0]].zero()
|
|
|
|
|
|
|
|
|
|
for _ in range(100):
|
|
|
|
|
if fixed_point.value:
|
|
|
|
|
break
|
|
|
|
|
fixed_point.value = True
|
|
|
|
|
|
|
|
|
|
for inst, next_inst in zip(instructions, instructions[1:] + [None]):
|
|
|
|
|
stack_size = stack_sizes[inst]
|
2023-06-29 20:23:17 +00:00
|
|
|
# CALL_FINALLY in Python 3.8 is handled differently when determining stack depth.
|
|
|
|
|
# See https://github.com/python/cpython/blob/3.8/Python/compile.c#L5450.
|
|
|
|
|
# Essentially, the stack effect of CALL_FINALLY is computed with jump=True,
|
|
|
|
|
# but the resulting stack depth is propagated to the next instruction, not the
|
|
|
|
|
# jump target.
|
|
|
|
|
is_call_finally = (
|
|
|
|
|
sys.version_info < (3, 9) and inst.opcode == dis.opmap["CALL_FINALLY"]
|
|
|
|
|
)
|
2022-10-13 23:18:06 +00:00
|
|
|
if inst.opcode not in TERMINAL_OPCODES:
|
|
|
|
|
assert next_inst is not None, f"missing next inst: {inst}"
|
2024-04-16 00:40:53 +00:00
|
|
|
# total stack effect of CALL_FINALLY and END_FINALLY in 3.8 is 0
|
|
|
|
|
eff = (
|
|
|
|
|
0
|
|
|
|
|
if is_call_finally
|
|
|
|
|
else stack_effect(inst.opcode, inst.arg, jump=False)
|
2022-10-13 23:18:06 +00:00
|
|
|
)
|
2024-04-16 00:40:53 +00:00
|
|
|
stack_sizes[next_inst].offset_of(stack_size, eff)
|
2023-06-29 20:23:17 +00:00
|
|
|
if inst.opcode in JUMP_OPCODES and not is_call_finally:
|
2022-10-13 23:18:06 +00:00
|
|
|
stack_sizes[inst.target].offset_of(
|
|
|
|
|
stack_size, stack_effect(inst.opcode, inst.arg, jump=True)
|
|
|
|
|
)
|
2023-05-08 23:26:19 +00:00
|
|
|
if inst.exn_tab_entry:
|
|
|
|
|
# see https://github.com/python/cpython/blob/3.11/Objects/exception_handling_notes.txt
|
|
|
|
|
# on why depth is computed this way.
|
|
|
|
|
depth = inst.exn_tab_entry.depth + int(inst.exn_tab_entry.lasti) + 1
|
|
|
|
|
stack_sizes[inst.exn_tab_entry.target].exn_tab_jump(depth)
|
2022-10-13 23:18:06 +00:00
|
|
|
|
|
|
|
|
if False:
|
|
|
|
|
for inst in instructions:
|
|
|
|
|
stack_size = stack_sizes[inst]
|
|
|
|
|
print(stack_size.low, stack_size.high, inst)
|
|
|
|
|
|
2024-04-12 23:54:11 +00:00
|
|
|
low = min(x.low for x in stack_sizes.values())
|
|
|
|
|
high = max(x.high for x in stack_sizes.values())
|
2022-10-13 23:18:06 +00:00
|
|
|
|
|
|
|
|
assert fixed_point.value, "failed to reach fixed point"
|
|
|
|
|
assert low >= 0
|
|
|
|
|
return high
|