@@ -95,6 +95,7 @@ class InstructionKind(enum.Enum):
9595 JUMP = enum .auto ()
9696 LONG_BRANCH = enum .auto ()
9797 SHORT_BRANCH = enum .auto ()
98+ CALL = enum .auto ()
9899 RETURN = enum .auto ()
99100 SMALL_CONST_1 = enum .auto ()
100101 SMALL_CONST_2 = enum .auto ()
@@ -182,6 +183,8 @@ class Optimizer:
182183 # Two groups (instruction and target):
183184 _re_branch : typing .ClassVar [re .Pattern [str ]] = _RE_NEVER_MATCH
184185 # One group (target):
186+ _re_call : typing .ClassVar [re .Pattern [str ]] = _RE_NEVER_MATCH
187+ # One group (target):
185188 _re_jump : typing .ClassVar [re .Pattern [str ]] = _RE_NEVER_MATCH
186189 # No groups:
187190 _re_return : typing .ClassVar [re .Pattern [str ]] = _RE_NEVER_MATCH
@@ -225,6 +228,11 @@ def __post_init__(self) -> None:
225228 assert inst .target is not None
226229 block .target = self ._lookup_label (inst .target )
227230 assert block .fallthrough
231+ elif inst .kind == InstructionKind .CALL :
232+ # A block ending in a call has a target and return point after call:
233+ assert inst .target is not None
234+ block .target = self ._lookup_label (inst .target )
235+ assert block .fallthrough
228236 elif inst .kind == InstructionKind .JUMP :
229237 # A block ending in a jump has a target and no fallthrough:
230238 assert inst .target is not None
@@ -256,6 +264,10 @@ def _parse_instruction(self, line: str) -> Instruction:
256264 target = match ["target" ]
257265 name = line [: - len (target )].strip ()
258266 kind = InstructionKind .JUMP
267+ elif match := self ._re_call .match (line ):
268+ target = match ["target" ]
269+ name = line [: - len (target )].strip ()
270+ kind = InstructionKind .CALL
259271 elif match := self ._re_return .match (line ):
260272 name = line
261273 kind = InstructionKind .RETURN
@@ -463,6 +475,8 @@ def _fixup_external_labels(self) -> None:
463475 for index , block in enumerate (self ._blocks ()):
464476 if block .target and block .fallthrough :
465477 branch = block .instructions [- 1 ]
478+ if branch .kind == InstructionKind .CALL :
479+ continue
466480 assert branch .is_branch ()
467481 target = branch .target
468482 assert target is not None
@@ -566,6 +580,8 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods
566580 rf"\s*(?P<instruction>{ '|' .join (_branch_patterns )} )\s+(.+,\s+)*(?P<target>[\w.]+)"
567581 )
568582
583+ # https://developer.arm.com/documentation/ddi0406/b/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate-
584+ _re_call = re .compile (r"\s*blx?\s+(?P<target>[\w.]+)" )
569585 # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch-
570586 _re_jump = re .compile (r"\s*b\s+(?P<target>[\w.]+)" )
571587 # https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine-
@@ -628,6 +644,8 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods
628644 _re_branch = re .compile (
629645 rf"\s*(?P<instruction>{ '|' .join (_X86_BRANCHES )} )\s+(?P<target>[\w.]+)"
630646 )
647+ # https://www.felixcloutier.com/x86/call
648+ _re_call = re .compile (r"\s*callq?\s+(?P<target>[\w.]+)" )
631649 # https://www.felixcloutier.com/x86/jmp
632650 _re_jump = re .compile (r"\s*jmp\s+(?P<target>[\w.]+)" )
633651 # https://www.felixcloutier.com/x86/ret
0 commit comments