mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	Introducing a new file, stacking.py, that takes over several responsibilities related to symbolic evaluation of push/pop operations, with more generality.
		
			
				
	
	
		
			102 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import dataclasses
 | 
						|
 | 
						|
from formatting import Formatter
 | 
						|
import lexer as lx
 | 
						|
import parsing
 | 
						|
 | 
						|
 | 
						|
@dataclasses.dataclass
 | 
						|
class InstructionFlags:
 | 
						|
    """Construct and manipulate instruction flags"""
 | 
						|
 | 
						|
    HAS_ARG_FLAG: bool
 | 
						|
    HAS_CONST_FLAG: bool
 | 
						|
    HAS_NAME_FLAG: bool
 | 
						|
    HAS_JUMP_FLAG: bool
 | 
						|
    HAS_FREE_FLAG: bool
 | 
						|
    HAS_LOCAL_FLAG: bool
 | 
						|
 | 
						|
    def __post_init__(self):
 | 
						|
        self.bitmask = {name: (1 << i) for i, name in enumerate(self.names())}
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def fromInstruction(instr: parsing.Node):
 | 
						|
 | 
						|
        has_free = (
 | 
						|
            variable_used(instr, "PyCell_New")
 | 
						|
            or variable_used(instr, "PyCell_GET")
 | 
						|
            or variable_used(instr, "PyCell_SET")
 | 
						|
        )
 | 
						|
 | 
						|
        return InstructionFlags(
 | 
						|
            HAS_ARG_FLAG=variable_used(instr, "oparg"),
 | 
						|
            HAS_CONST_FLAG=variable_used(instr, "FRAME_CO_CONSTS"),
 | 
						|
            HAS_NAME_FLAG=variable_used(instr, "FRAME_CO_NAMES"),
 | 
						|
            HAS_JUMP_FLAG=variable_used(instr, "JUMPBY"),
 | 
						|
            HAS_FREE_FLAG=has_free,
 | 
						|
            HAS_LOCAL_FLAG=(
 | 
						|
                variable_used(instr, "GETLOCAL") or variable_used(instr, "SETLOCAL")
 | 
						|
            )
 | 
						|
            and not has_free,
 | 
						|
        )
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def newEmpty():
 | 
						|
        return InstructionFlags(False, False, False, False, False, False)
 | 
						|
 | 
						|
    def add(self, other: "InstructionFlags") -> None:
 | 
						|
        for name, value in dataclasses.asdict(other).items():
 | 
						|
            if value:
 | 
						|
                setattr(self, name, value)
 | 
						|
 | 
						|
    def names(self, value=None) -> list[str]:
 | 
						|
        if value is None:
 | 
						|
            return list(dataclasses.asdict(self).keys())
 | 
						|
        return [n for n, v in dataclasses.asdict(self).items() if v == value]
 | 
						|
 | 
						|
    def bitmap(self) -> int:
 | 
						|
        flags = 0
 | 
						|
        for name in self.names():
 | 
						|
            if getattr(self, name):
 | 
						|
                flags |= self.bitmask[name]
 | 
						|
        return flags
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def emit_macros(cls, out: Formatter):
 | 
						|
        flags = cls.newEmpty()
 | 
						|
        for name, value in flags.bitmask.items():
 | 
						|
            out.emit(f"#define {name} ({value})")
 | 
						|
 | 
						|
        for name, value in flags.bitmask.items():
 | 
						|
            out.emit(
 | 
						|
                f"#define OPCODE_{name[:-len('_FLAG')]}(OP) "
 | 
						|
                f"(_PyOpcode_opcode_metadata[OP].flags & ({name}))"
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
def variable_used(node: parsing.Node, name: str) -> bool:
 | 
						|
    """Determine whether a variable with a given name is used in a node."""
 | 
						|
    return any(
 | 
						|
        token.kind == "IDENTIFIER" and token.text == name for token in node.tokens
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def variable_used_unspecialized(node: parsing.Node, name: str) -> bool:
 | 
						|
    """Like variable_used(), but skips #if ENABLE_SPECIALIZATION blocks."""
 | 
						|
    tokens: list[lx.Token] = []
 | 
						|
    skipping = False
 | 
						|
    for i, token in enumerate(node.tokens):
 | 
						|
        if token.kind == "MACRO":
 | 
						|
            text = "".join(token.text.split())
 | 
						|
            # TODO: Handle nested #if
 | 
						|
            if text == "#if":
 | 
						|
                if (
 | 
						|
                    i + 1 < len(node.tokens)
 | 
						|
                    and node.tokens[i + 1].text == "ENABLE_SPECIALIZATION"
 | 
						|
                ):
 | 
						|
                    skipping = True
 | 
						|
            elif text in ("#else", "#endif"):
 | 
						|
                skipping = False
 | 
						|
        if not skipping:
 | 
						|
            tokens.append(token)
 | 
						|
    return any(token.kind == "IDENTIFIER" and token.text == name for token in tokens)
 |