summaryrefslogtreecommitdiff
path: root/tools/asm-differ
diff options
context:
space:
mode:
authorEllipticEllipsis <73679967+EllipticEllipsis@users.noreply.github.com>2022-03-31 18:22:19 +0100
committerGitHub <noreply@github.com>2022-03-31 18:22:19 +0100
commit15dfaf0862e761b97edcda9c1dcc01639cfba5f4 (patch)
tree953f08d3ff5e07c9df094191c413d39b76948dfa /tools/asm-differ
parent54a4d1eb1202c912f6c3b269e6223ea189e7c42c (diff)
ABI FPR names (#767)
* Add o32 to disassembler and update macro.inc * Add a variable for asm processor to makefile and improve objdump flags * Update diff settings * git subrepo pull --force tools/asm-differ subrepo: subdir: "tools/asm-differ" merged: "1236288d1" upstream: origin: "https://github.com/simonlindholm/asm-differ" branch: "main" commit: "1236288d1" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596" * Remove * import and implement option * Fix some stuff in the makefile * Update asm-processor * Review * Fix old var name
Diffstat (limited to 'tools/asm-differ')
-rw-r--r--tools/asm-differ/.gitrepo4
-rw-r--r--tools/asm-differ/.pre-commit-config.yaml3
-rw-r--r--tools/asm-differ/README.md4
-rwxr-xr-xtools/asm-differ/diff.py196
4 files changed, 143 insertions, 64 deletions
diff --git a/tools/asm-differ/.gitrepo b/tools/asm-differ/.gitrepo
index 48e215c08..8d0a6445d 100644
--- a/tools/asm-differ/.gitrepo
+++ b/tools/asm-differ/.gitrepo
@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/simonlindholm/asm-differ
branch = main
- commit = 6f8f80b719359d018a2b734288c977aae6538870
- parent = 91a6e9f647d2035eba281d286c78f089f70f269f
+ commit = 1236288d1520335c2bfb672078fec65084d7cb5c
+ parent = 2c5690701a350c7e7c3d6252dff925ad65d59910
method = merge
cmdver = 0.4.3
diff --git a/tools/asm-differ/.pre-commit-config.yaml b/tools/asm-differ/.pre-commit-config.yaml
index 6695f71ac..ccc4afc0e 100644
--- a/tools/asm-differ/.pre-commit-config.yaml
+++ b/tools/asm-differ/.pre-commit-config.yaml
@@ -1,6 +1,5 @@
repos:
- repo: https://github.com/psf/black
- rev: 20.8b1
+ rev: 22.1.0
hooks:
- id: black
- language_version: python3.6
diff --git a/tools/asm-differ/README.md b/tools/asm-differ/README.md
index 4a7f329b3..205310d39 100644
--- a/tools/asm-differ/README.md
+++ b/tools/asm-differ/README.md
@@ -11,10 +11,10 @@ Nice differ for assembly code. Currently supports MIPS, PPC, AArch64, and ARM32;
## Usage
-Create a file `diff_settings.sh` in some directory (see the one in this repo for an example). Then from that directory, run
+Create a file `diff_settings.py` in some directory (see the one in this repo for an example). Then from that directory, run
```bash
-/path/to/diff.sh [flags] (function|rom addr)
+/path/to/diff.py [flags] (function|rom addr)
```
Recommended flags are `-mwo` (automatically run `make` on source file changes, and include symbols in diff). See `--help` for more details.
diff --git a/tools/asm-differ/diff.py b/tools/asm-differ/diff.py
index 9781420bb..4b5bc892e 100755
--- a/tools/asm-differ/diff.py
+++ b/tools/asm-differ/diff.py
@@ -110,6 +110,15 @@ if __name__ == "__main__":
see symbol names. (Recommended)""",
)
parser.add_argument(
+ "-f",
+ "--objfile",
+ dest="objfile",
+ type=str,
+ help="""File path for an object file being diffed. When used
+ the map file isn't searched for the function given. Useful for dynamically
+ linked libraries.""",
+ )
+ parser.add_argument(
"-e",
"--elf",
dest="diff_elf_symbol",
@@ -133,6 +142,14 @@ if __name__ == "__main__":
Implies --source.""",
)
parser.add_argument(
+ "-j",
+ "--section",
+ dest="diff_section",
+ default=".text",
+ metavar="SECTION",
+ help="Diff restricted to a given output section.",
+ )
+ parser.add_argument(
"-L",
"--line-numbers",
dest="show_line_numbers",
@@ -356,6 +373,7 @@ except ModuleNotFoundError as e:
class ProjectSettings:
arch_str: str
objdump_executable: str
+ objdump_flags: List[str]
build_command: List[str]
map_format: str
mw_build_dir: str
@@ -365,6 +383,7 @@ class ProjectSettings:
source_directories: Optional[List[str]]
source_extensions: List[str]
show_line_numbers_default: bool
+ disassemble_all: bool
@dataclass
@@ -379,8 +398,10 @@ class Config:
# Build/objdump options
diff_obj: bool
+ objfile: Optional[str]
make: bool
source_old_binutils: bool
+ diff_section: str
inlines: bool
max_function_size_lines: int
max_function_size_bytes: int
@@ -422,9 +443,11 @@ def create_project_settings(settings: Dict[str, Any]) -> ProjectSettings:
"source_extensions", [".c", ".h", ".cpp", ".hpp", ".s"]
),
objdump_executable=get_objdump_executable(settings.get("objdump_executable")),
+ objdump_flags=settings.get("objdump_flags", []),
map_format=settings.get("map_format", "gnu"),
mw_build_dir=settings.get("mw_build_dir", "build/"),
show_line_numbers_default=settings.get("show_line_numbers_default", True),
+ disassemble_all=settings.get("disassemble_all", False),
)
@@ -461,8 +484,10 @@ def create_config(args: argparse.Namespace, project: ProjectSettings) -> Config:
arch=arch,
# Build/objdump options
diff_obj=args.diff_obj,
+ objfile=args.objfile,
make=args.make,
source_old_binutils=args.source_old_binutils,
+ diff_section=args.diff_section,
inlines=args.inlines,
max_function_size_lines=args.max_lines,
max_function_size_bytes=args.max_lines * 4,
@@ -518,7 +543,7 @@ def get_arch(arch_str: str) -> "ArchSettings":
raise ValueError(f"Unknown architecture: {arch_str}")
-BUFFER_CMD: List[str] = ["tail", "-c", str(10 ** 9)]
+BUFFER_CMD: List[str] = ["tail", "-c", str(10**9)]
# -S truncates long lines instead of wrapping them
# -R interprets color escape sequences
@@ -809,12 +834,7 @@ class JsonFormatter(Formatter):
return {"text": s, "format": f.name.lower()}
elif isinstance(f, RotationFormat):
attrs = asdict(f)
- attrs.update(
- {
- "text": s,
- "format": "rotation",
- }
- )
+ attrs.update({"text": s, "format": "rotation"})
return attrs
else:
static_assert_unreachable(f)
@@ -962,7 +982,7 @@ def restrict_to_function(dump: str, fn_name: str) -> str:
return ""
-def serialize_data_references(references: List[Tuple[int, int, str]]) -> str:
+def serialize_rodata_references(references: List[Tuple[int, int, str]]) -> str:
return "".join(
f"DATAREF {text_offset} {from_offset} {from_section}\n"
for (text_offset, from_offset, from_section) in references
@@ -991,7 +1011,11 @@ def run_objdump(cmd: ObjdumpCommand, config: Config, project: ProjectSettings) -
flags, target, restrict = cmd
try:
out = subprocess.run(
- [project.objdump_executable] + config.arch.arch_flags + flags + [target],
+ [project.objdump_executable]
+ + config.arch.arch_flags
+ + project.objdump_flags
+ + flags
+ + [target],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
@@ -1009,11 +1033,11 @@ def run_objdump(cmd: ObjdumpCommand, config: Config, project: ProjectSettings) -
with open(target, "rb") as f:
obj_data = f.read()
- return preprocess_objdump_out(restrict, obj_data, out)
+ return preprocess_objdump_out(restrict, obj_data, out, config)
def preprocess_objdump_out(
- restrict: Optional[str], obj_data: Optional[bytes], objdump_out: str
+ restrict: Optional[str], obj_data: Optional[bytes], objdump_out: str, config: Config
) -> str:
"""
Preprocess the output of objdump into a format that `process()` expects.
@@ -1033,13 +1057,16 @@ def preprocess_objdump_out(
out = out.rstrip("\n")
if obj_data:
- out = serialize_data_references(parse_elf_data_references(obj_data)) + out
+ out = (
+ serialize_rodata_references(parse_elf_rodata_references(obj_data, config))
+ + out
+ )
return out
def search_map_file(
- fn_name: str, project: ProjectSettings
+ fn_name: str, project: ProjectSettings, config: Config
) -> Tuple[Optional[str], Optional[int]]:
if not project.mapfile:
fail(f"No map file configured; cannot find function {fn_name}.")
@@ -1059,7 +1086,7 @@ def search_map_file(
cands = []
last_line = ""
for line in lines:
- if line.startswith(" .text"):
+ if line.startswith(" " + config.diff_section):
cur_objfile = line.split()[3]
if "load address" in line:
tokens = last_line.split() + line.split()
@@ -1084,9 +1111,12 @@ def search_map_file(
re.compile(
# ram elf rom
r" \S+ \S+ (\S+) (\S+) . "
- + fn_name
- # object name
- + r"(?: \(entry of \.(?:init|text)\))? \t(\S+)"
+ + re.escape(fn_name)
+ + r"(?: \(entry of "
+ + re.escape(config.diff_section)
+ + r"\))? \t"
+ # object name
+ + "(\S+)"
),
contents,
)
@@ -1121,7 +1151,9 @@ def search_map_file(
return None, None
-def parse_elf_data_references(data: bytes) -> List[Tuple[int, int, str]]:
+def parse_elf_rodata_references(
+ data: bytes, config: Config
+) -> List[Tuple[int, int, str]]:
e_ident = data[:16]
if e_ident[:4] != b"\x7FELF":
return []
@@ -1134,7 +1166,6 @@ def parse_elf_data_references(data: bytes) -> List[Tuple[int, int, str]]:
is_little_endian = e_ident[5] == 1
str_end = "<" if is_little_endian else ">"
str_off = "I" if is_32bit else "Q"
- sym_size = {"B": 1, "H": 2, "I": 4, "Q": 8}
def read(spec: str, offset: int) -> Tuple[int, ...]:
spec = spec.replace("P", str_off)
@@ -1186,7 +1217,12 @@ def parse_elf_data_references(data: bytes) -> List[Tuple[int, int, str]]:
assert len(symtab_sections) == 1
symtab = sections[symtab_sections[0]]
- text_sections = [i for i in range(e_shnum) if sec_names[i] == b".text" and sections[i].sh_size != 0]
+ section_name = config.diff_section.encode("utf-8")
+ text_sections = [
+ i
+ for i in range(e_shnum)
+ if sec_names[i] == section_name and sections[i].sh_size != 0
+ ]
if len(text_sections) != 1:
return []
text_section = text_sections[0]
@@ -1195,11 +1231,10 @@ def parse_elf_data_references(data: bytes) -> List[Tuple[int, int, str]]:
for s in sections:
if s.sh_type == SHT_REL or s.sh_type == SHT_RELA:
if s.sh_info == text_section:
- # Skip .text -> .text references
+ # Skip section_name -> section_name references
continue
sec_name = sec_names[s.sh_info].decode("latin1")
- if sec_name == ".mwcats.text":
- # Skip Metrowerks CATS Utility section
+ if sec_name != ".rodata":
continue
sec_base = sections[s.sh_info].sh_offset
for i in range(0, s.sh_size, s.sh_entsize):
@@ -1257,11 +1292,16 @@ def dump_elf(
f"--stop-address={end_addr}",
]
+ if project.disassemble_all:
+ disassemble_flag = "-D"
+ else:
+ disassemble_flag = "-d"
+
flags2 = [
f"--disassemble={diff_elf_symbol}",
]
- objdump_flags = ["-drz", "-j", ".text"]
+ objdump_flags = [disassemble_flag, "-rz", "-j", config.diff_section]
return (
project.myimg,
(objdump_flags + flags1, project.baseimg, None),
@@ -1283,7 +1323,10 @@ def dump_objfile(
if start.startswith("0"):
fail("numerical start address not supported with -o; pass a function name")
- objfile, _ = search_map_file(start, project)
+ objfile = config.objfile
+ if not objfile:
+ objfile, _ = search_map_file(start, project, config)
+
if not objfile:
fail("Not able to find .o file for function.")
@@ -1297,7 +1340,12 @@ def dump_objfile(
if not os.path.isfile(refobjfile):
fail(f'Please ensure an OK .o file exists at "{refobjfile}".')
- objdump_flags = ["-drz", "-j", ".text"]
+ if project.disassemble_all:
+ disassemble_flag = "-D"
+ else:
+ disassemble_flag = "-d"
+
+ objdump_flags = [disassemble_flag, "-rz", "-j", config.diff_section]
return (
objfile,
(objdump_flags, refobjfile, start),
@@ -1314,7 +1362,7 @@ def dump_binary(
run_make(project.myimg, project)
start_addr = maybe_eval_int(start)
if start_addr is None:
- _, start_addr = search_map_file(start, project)
+ _, start_addr = search_map_file(start, project, config)
if start_addr is None:
fail("Not able to find function in map file.")
if end is not None:
@@ -1333,6 +1381,7 @@ def dump_binary(
(objdump_flags + flags2, project.myimg, None),
)
+
# Example: "ldr r4, [pc, #56] ; (4c <AddCoins+0x4c>)"
ARM32_LOAD_POOL_PATTERN = r"(ldr\s+r([0-9]|1[0-3]),\s+\[pc,.*;\s*)(\([a-fA-F0-9]+.*\))"
@@ -1363,7 +1412,7 @@ class AsmProcessor:
class AsmProcessorMIPS(AsmProcessor):
def process_reloc(self, row: str, prev: str) -> str:
arch = self.config.arch
- if "R_MIPS_NONE" in row:
+ if "R_MIPS_NONE" in row or "R_MIPS_JALR" in row:
# GNU as emits no-op relocations immediately after real ones when
# assembling with -mabi=64. Return without trying to parse 'imm' as an
# integer.
@@ -1378,12 +1427,9 @@ class AsmProcessorMIPS(AsmProcessor):
# TODO: handle unambiguous cases where all addends for a symbol are the
# same, or show "+???".
mnemonic = prev.split()[0]
- if (
- mnemonic in arch.instructions_with_address_immediates
- and not imm.startswith("0x")
- ):
- imm = "0x" + imm
- repl += "+" + imm if int(imm, 0) > 0 else imm
+ if mnemonic in arch.instructions_with_address_immediates:
+ imm = hex(int(imm, 16))
+ repl += ("" if imm.startswith("-") else "+") + imm
if "R_MIPS_LO16" in row:
repl = f"%lo({repl})"
elif "R_MIPS_HI16" in row:
@@ -1398,6 +1444,12 @@ class AsmProcessorMIPS(AsmProcessor):
# Branch to glabel. This gives confusing output, but there's not much
# we can do here.
pass
+ elif "R_MIPS_GPREL16" in row:
+ repl = f"%gp_rel({repl})"
+ elif "R_MIPS_GOT16" in row:
+ repl = f"%got({repl})"
+ elif "R_MIPS_CALL16" in row:
+ repl = f"%call16({repl})"
else:
assert False, f"unknown relocation type '{row}' for line '{prev}'"
return before + repl + after
@@ -1544,6 +1596,7 @@ class ArchSettings:
big_endian: Optional[bool] = True
delay_slot_instructions: Set[str] = field(default_factory=set)
+
MIPS_BRANCH_LIKELY_INSTRUCTIONS = {
"beql",
"bnel",
@@ -1651,10 +1704,13 @@ PPC_BRANCH_INSTRUCTIONS = {
MIPS_SETTINGS = ArchSettings(
name="mips",
re_int=re.compile(r"[0-9]+"),
- re_comment=re.compile(r"<.*?>"),
- re_reg=re.compile(
- r"\$?\b(a[0-7]|t[0-9]|s[0-8]|at|v[01]|f[12]?[0-9]|f3[01]|kt?[01]|fp|ra|zero)\b"
- ),
+ re_comment=re.compile(r"<.*>"),
+ # Includes:
+ # - General purpose registers v0..1, a0..7, t0..9, s0..8, zero, at, fp, k0..1/kt0..1
+ # - Float registers f0..31, or fv0..1, fa0..7, ft0..15, fs0..8 plus odd complements
+ # (actually used number depends on ABI)
+ # sp, gp should not be in this list
+ re_reg=re.compile(r"\$?\b([astv][0-9]|at|f[astv]?[0-9]+f?|kt?[01]|fp|ra|zero)\b"),
re_sprel=re.compile(r"(?<=,)([0-9]+|0x[0-9a-f]+)\(sp\)"),
re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"),
re_imm=re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)"),
@@ -1672,7 +1728,7 @@ MIPSEL_SETTINGS = replace(MIPS_SETTINGS, name="mipsel", big_endian=False)
ARM32_SETTINGS = ArchSettings(
name="arm32",
re_int=re.compile(r"[0-9]+"),
- re_comment=re.compile(r"(<.*?>|//.*$)"),
+ re_comment=re.compile(r"(<.*>|//.*$)"),
# Includes:
# - General purpose registers: r0..13
# - Frame pointer registers: lr (r14), pc (r15)
@@ -1690,26 +1746,33 @@ ARM32_SETTINGS = ArchSettings(
proc=AsmProcessorARM32,
)
+ARMEL_SETTINGS = replace(ARM32_SETTINGS, name="armel", big_endian=False)
+
AARCH64_SETTINGS = ArchSettings(
name="aarch64",
re_int=re.compile(r"[0-9]+"),
- re_comment=re.compile(r"(<.*?>|//.*$)"),
- # GPRs and FP registers: X0-X30, W0-W30, [DSHQ]0..31
+ re_comment=re.compile(r"(<.*>|//.*$)"),
+ # GPRs and FP registers: X0-X30, W0-W30, [BHSDVQ]0..31
+ # (FP registers may be followed by data width and number of elements, e.g. V0.4S)
# The zero registers and SP should not be in this list.
- re_reg=re.compile(r"\$?\b([dshq][12]?[0-9]|[dshq]3[01]|[xw][12]?[0-9]|[xw]30)\b"),
+ re_reg=re.compile(
+ r"\$?\b([bhsdvq]([12]?[0-9]|3[01])(\.\d\d?[bhsdvq])?|[xw][12]?[0-9]|[xw]30)\b"
+ ),
re_sprel=re.compile(r"sp, #-?(0x[0-9a-fA-F]+|[0-9]+)\b"),
re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"),
re_imm=re.compile(r"(?<!sp, )#-?(0x[0-9a-fA-F]+|[0-9]+)\b"),
re_reloc=re.compile(r"R_AARCH64_"),
branch_instructions=AARCH64_BRANCH_INSTRUCTIONS,
- instructions_with_address_immediates=AARCH64_BRANCH_INSTRUCTIONS.union({"bl", "adrp"}),
+ instructions_with_address_immediates=AARCH64_BRANCH_INSTRUCTIONS.union(
+ {"bl", "adrp"}
+ ),
proc=AsmProcessorAArch64,
)
PPC_SETTINGS = ArchSettings(
name="ppc",
re_int=re.compile(r"[0-9]+"),
- re_comment=re.compile(r"(<.*?>|//.*$)"),
+ re_comment=re.compile(r"(<.*>|//.*$)"),
re_reg=re.compile(r"\$?\b([rf][0-9]+)\b"),
re_sprel=re.compile(r"(?<=,)(-?[0-9]+|-?0x[0-9a-f]+)\(r1\)"),
re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"),
@@ -1724,6 +1787,7 @@ ARCH_SETTINGS = [
MIPS_SETTINGS,
MIPSEL_SETTINGS,
ARM32_SETTINGS,
+ ARMEL_SETTINGS,
AARCH64_SETTINGS,
PPC_SETTINGS,
]
@@ -1743,6 +1807,7 @@ def hexify_int(row: str, pat: Match[str], arch: ArchSettings) -> str:
def parse_relocated_line(line: str) -> Tuple[str, str, str]:
+ # Pick out the last argument
for c in ",\t ":
if c in line:
ind2 = line.rindex(c)
@@ -1751,6 +1816,7 @@ def parse_relocated_line(line: str) -> Tuple[str, str, str]:
raise Exception(f"failed to parse relocated line: {line}")
before = line[: ind2 + 1]
after = line[ind2 + 1 :]
+ # Move an optional ($reg) part of it to 'after'
ind2 = after.find("(")
if ind2 == -1:
imm, after = after, ""
@@ -1879,8 +1945,15 @@ def process(dump: str, config: Config) -> List[Line]:
row_parts = [part.lstrip() for part in row.split(" ", 1)]
mnemonic = row_parts[0].strip()
- if mnemonic not in arch.instructions_with_address_immediates:
- row = re.sub(arch.re_int, lambda m: hexify_int(row, m, arch), row)
+ addr = ""
+ if mnemonic in arch.instructions_with_address_immediates:
+ row, addr = split_off_address(row)
+ # objdump prefixes addresses with 0x/-0x if they don't resolve to some
+ # symbol + offset. Strip that.
+ addr = addr.replace("0x", "")
+
+ row = re.sub(arch.re_int, lambda m: hexify_int(row, m, arch), row)
+ row += addr
# Let 'original' be 'row' with relocations applied, while we continue
# transforming 'row' into a coarser version that ignores registers and
@@ -1900,9 +1973,6 @@ def process(dump: str, config: Config) -> List[Line]:
scorable_line = normalized_original
if not config.score_stack_differences:
scorable_line = re.sub(arch.re_sprel, "addr(sp)", scorable_line)
- if mnemonic in arch.branch_instructions:
- # Replace the final argument with "<target>"
- scorable_line = re.sub(r"[^, \t]+$", "<target>", scorable_line)
if skip_next:
skip_next = False
@@ -1925,8 +1995,6 @@ def process(dump: str, config: Config) -> List[Line]:
branch_target = None
if mnemonic in arch.branch_instructions:
branch_target = int(row_parts[1].strip().split(",")[-1], 16)
- if mnemonic in arch.branch_likely_instructions:
- branch_target -= 4
output.append(
Line(
@@ -1974,7 +2042,9 @@ def split_off_address(line: str) -> Tuple[str, str]:
parts = line.split(",")
if len(parts) < 2:
parts = line.split(None, 1)
- off = len(line) - len(parts[-1])
+ if len(parts) < 2:
+ parts.append("")
+ off = len(line) - len(parts[-1].strip())
return line[:off], line[off:]
@@ -1990,7 +2060,7 @@ def diff_sequences(
) -> List[Tuple[str, int, int, int, int]]:
if (
algorithm != "levenshtein"
- or len(seq1) * len(seq2) > 4 * 10 ** 8
+ or len(seq1) * len(seq2) > 4 * 10**8
or len(seq1) + len(seq2) >= 0x110000
):
return diff_sequences_difflib(seq1, seq2)
@@ -2177,10 +2247,15 @@ class Diff:
def trim_nops(lines: List[Line], arch: ArchSettings) -> List[Line]:
lines = lines[:]
- while lines and lines[-1].mnemonic == "nop" and (len(lines) == 1 or lines[-2].mnemonic not in arch.delay_slot_instructions):
+ while (
+ lines
+ and lines[-1].mnemonic == "nop"
+ and (len(lines) == 1 or lines[-2].mnemonic not in arch.delay_slot_instructions)
+ ):
lines.pop()
return lines
+
def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
if config.show_source:
import cxxfilt
@@ -2212,7 +2287,6 @@ def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
lines2 = trim_nops(lines2, arch)
diffed_lines = diff_lines(lines1, lines2, config.algorithm)
- score = score_diff_lines(diffed_lines, config)
max_score = len(lines1) * config.penalty_deletion
line_num_base = -1
@@ -2281,10 +2355,15 @@ def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
line2_line = line_num_2to1[line2.line_num]
line2_target = (line2_line[0] + (target - line2.line_num), 0)
- # Set the key for three-way diffing to a normalized version.
+ # Adjust the branch target for scoring and three-way diffing.
norm2, norm_branch2 = split_off_address(line2.normalized_original)
- if norm_branch2 != "<ign>":
- line2.normalized_original = norm2 + str(line2_target)
+ if norm_branch2 != "<ignore>":
+ retargetted = hex(line2_target[0]).replace("0x", "")
+ if line2_target[1] != 0:
+ retargetted += f"+{line2_target[1]}"
+ line2.normalized_original = norm2 + retargetted
+ sc_base, _ = split_off_address(line2.scorable_line)
+ line2.scorable_line = sc_base + retargetted
same_target = line2_target == (line1.branch_target, 0)
else:
# Do a naive comparison for non-branches (e.g. function calls).
@@ -2444,6 +2523,7 @@ def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff:
)
)
+ score = score_diff_lines(diffed_lines, config)
output = output[config.skip_lines :]
return Diff(lines=output, score=score, max_score=max_score)