|
6 | 6 | from typing import Any, Dict, List, Optional, Set, Tuple, Union |
7 | 7 | from pathlib import Path |
8 | 8 |
|
| 9 | +from collections import defaultdict, deque |
| 10 | + |
9 | 11 | from .. import __package_name__, __version__ |
10 | 12 | from ..disassembler import disassembler_instance |
11 | 13 | from ..util import cache_handler, progress_bar, vram_classes, statistics, file_presets |
@@ -222,6 +224,47 @@ def calc_segment_dependences( |
222 | 224 | return vram_class_to_follows_segments |
223 | 225 |
|
224 | 226 |
|
| 227 | +def sort_segments_by_vram_class_dependency( |
| 228 | + all_segments: List[Segment], |
| 229 | +) -> List[Segment]: |
| 230 | + # map all "_VRAM_END" strings to segments |
| 231 | + end_sym_to_seg: Dict[str, Segment] = {} |
| 232 | + for seg in all_segments: |
| 233 | + end_sym_to_seg[get_segment_vram_end_symbol_name(seg)] = seg |
| 234 | + |
| 235 | + # build dependency graph: A -> B means "A must come before B" |
| 236 | + graph: Dict[Segment, List[Segment]] = defaultdict(list) |
| 237 | + indeg: Dict[Segment, int] = {seg: 0 for seg in all_segments} |
| 238 | + |
| 239 | + for seg in all_segments: |
| 240 | + sym = seg.vram_symbol |
| 241 | + if sym is None: |
| 242 | + continue |
| 243 | + dep = end_sym_to_seg.get(sym) |
| 244 | + if dep is None or dep is seg: |
| 245 | + continue |
| 246 | + graph[dep].append(seg) |
| 247 | + indeg[seg] += 1 |
| 248 | + |
| 249 | + # stable topo sort with queue seeded in original order |
| 250 | + q = deque([seg for seg in all_segments if indeg[seg] == 0]) |
| 251 | + out: List[Segment] = [] |
| 252 | + |
| 253 | + while q: |
| 254 | + n = q.popleft() |
| 255 | + out.append(n) |
| 256 | + for m in graph.get(n, []): |
| 257 | + indeg[m] -= 1 |
| 258 | + if indeg[m] == 0: |
| 259 | + q.append(m) |
| 260 | + |
| 261 | + assert len(out) == len(all_segments), ( |
| 262 | + "Encountered cyclic dependency when reordering segments by vram class." |
| 263 | + ) |
| 264 | + |
| 265 | + return out |
| 266 | + |
| 267 | + |
225 | 268 | def read_target_binary() -> bytes: |
226 | 269 | rom_bytes = options.opts.target_path.read_bytes() |
227 | 270 |
|
@@ -317,6 +360,9 @@ def do_split( |
317 | 360 |
|
318 | 361 |
|
319 | 362 | def write_linker_script(all_segments: List[Segment]) -> LinkerWriter: |
| 363 | + if options.opts.ld_sort_segments_by_vram_class_dependency: |
| 364 | + all_segments = sort_segments_by_vram_class_dependency(all_segments) |
| 365 | + |
320 | 366 | vram_class_dependencies = calc_segment_dependences(all_segments) |
321 | 367 | vram_classes_to_search = set(vram_class_dependencies.keys()) |
322 | 368 |
|
|
0 commit comments