(self)
| 197 | _frame_pointer_modify: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
| 198 | |
| 199 | def __post_init__(self) -> None: |
| 200 | # Split the code into a linked list of basic blocks. A basic block is an |
| 201 | # optional label, followed by zero or more non-instruction lines, |
| 202 | # followed by zero or more instruction lines (only the last of which may |
| 203 | # be a branch, jump, or return): |
| 204 | self.text = self._preprocess(self.path.read_text()) |
| 205 | block = self._root |
| 206 | for line in self.text.splitlines(): |
| 207 | # See if we need to start a new block: |
| 208 | if match := self._re_label.match(line): |
| 209 | # Label. New block: |
| 210 | block.link = block = self._lookup_label(match["label"]) |
| 211 | block.noninstructions.append(line) |
| 212 | continue |
| 213 | if match := self.re_global.match(line): |
| 214 | self.globals.add(match["label"]) |
| 215 | block.noninstructions.append(line) |
| 216 | continue |
| 217 | if self._re_noninstructions.match(line): |
| 218 | if block.instructions: |
| 219 | # Non-instruction lines. New block: |
| 220 | block.link = block = _Block() |
| 221 | block.noninstructions.append(line) |
| 222 | continue |
| 223 | if block.target or not block.fallthrough: |
| 224 | # Current block ends with a branch, jump, or return. New block: |
| 225 | block.link = block = _Block() |
| 226 | inst = self._parse_instruction(line) |
| 227 | block.instructions.append(inst) |
| 228 | if inst.is_branch(): |
| 229 | # A block ending in a branch has a target and fallthrough: |
| 230 | assert inst.target is not None |
| 231 | block.target = self._lookup_label(inst.target) |
| 232 | assert block.fallthrough |
| 233 | elif inst.kind == InstructionKind.CALL: |
| 234 | # A block ending in a call has a target and return point after call: |
| 235 | assert inst.target is not None |
| 236 | block.target = self._lookup_label(inst.target) |
| 237 | assert block.fallthrough |
| 238 | elif inst.kind == InstructionKind.JUMP: |
| 239 | # A block ending in a jump has a target and no fallthrough: |
| 240 | assert inst.target is not None |
| 241 | block.target = self._lookup_label(inst.target) |
| 242 | block.fallthrough = False |
| 243 | elif inst.kind == InstructionKind.RETURN: |
| 244 | # A block ending in a return has no target and fallthrough: |
| 245 | assert not block.target |
| 246 | block.fallthrough = False |
| 247 | |
| 248 | def _preprocess(self, text: str) -> str: |
| 249 | # Override this method to do preprocessing of the textual assembly. |
nothing calls this directly
no test coverage detected