zingen/jump/target.rs
1//! Target related operations of the jump table.
2//!
3//! This module provides functions to retrieve and shift the target program counters
4//! associated with various jump types.
5
6use crate::{
7 jump::{Jump, JumpTable},
8 Error, Result,
9};
10
11impl JumpTable {
12 /// Retrieves the target of a given jump.
13 ///
14 /// This function returns the target program counter based on the type of jump
15 /// (offset, label, function, or external function).
16 pub fn target(&self, jump: &Jump) -> Result<u16> {
17 match jump {
18 Jump::Label(label) => Ok(*label),
19 Jump::Func(func) => Ok(*self.func.get(func).ok_or(Error::FuncNotFound(*func))?),
20 Jump::ExtFunc(ext) => Ok(self.code.offset_of(ext).ok_or(Error::ExtFuncNotFound)?),
21 }
22 }
23
24 /// Shifts the target program counters as a pre-calculation step.
25 ///
26 /// This function calculates the target program counter from the original program
27 /// counter and the target, adjusting for any offsets.
28 pub fn shift_targets(&mut self) -> Result<()> {
29 let mut total_offset = 0;
30 let mut target_sizes = Vec::new();
31 let jumps = self.jump.clone();
32
33 // First pass: calculate all target sizes and accumulate offsets
34 for (original_pc, jump) in jumps.iter() {
35 let pc = original_pc + total_offset;
36 let raw_target = self.target(jump)?;
37
38 // Calculate the absolute target including future offsets
39 let target = if raw_target > *original_pc {
40 raw_target + total_offset
41 } else {
42 raw_target
43 };
44
45 // Calculate instruction size based on absolute target value
46 let instr_size = if target > 0xff {
47 3 // PUSH2 + 2 bytes
48 } else {
49 2 // PUSH1 + 1 byte
50 };
51
52 target_sizes.push((pc, instr_size));
53 total_offset += instr_size;
54 }
55
56 // Second pass: apply shifts with accumulated offsets
57 total_offset = 0;
58 for (pc, size) in target_sizes {
59 tracing::debug!("shift target at pc=0x{pc:x} with size={size}");
60 self.shift_target(pc, size)?;
61 total_offset += size;
62 }
63
64 Ok(())
65 }
66
67 /// Shifts the program counter of targets with the given pointer and offset.
68 ///
69 /// This function handles the shifting of the code section, label targets, and
70 /// function targets.
71 pub fn shift_target(&mut self, ptr: u16, offset: u16) -> Result<()> {
72 // First shift the code section
73 self.code.shift(offset);
74
75 // Only shift targets that are after ptr
76 self.shift_label_target(ptr, offset)?;
77 self.shift_func_target(ptr, offset)
78 }
79
80 /// Shifts the program counter for functions.
81 pub fn shift_func_target(&mut self, ptr: u16, offset: u16) -> Result<()> {
82 self.func.iter_mut().try_for_each(|(index, target)| {
83 if *target > ptr {
84 let next_target = *target + offset;
85 tracing::trace!(
86 "shift Func({index}) target with offset={offset}: 0x{target:x}(0x{ptr:x}) -> 0x{:x}",
87 next_target
88 );
89
90 *target = next_target;
91 }
92
93 Ok(())
94 })
95 }
96
97 /// Shifts the program counter for labels.
98 pub fn shift_label_target(&mut self, ptr: u16, offset: u16) -> Result<()> {
99 for (_, jump) in self.jump.iter_mut() {
100 let Jump::Label(target) = jump else {
101 continue;
102 };
103
104 // Only shift targets that come after ptr AND
105 // only if the jump instruction itself comes after ptr
106 if *target > ptr {
107 let next_target = *target + offset;
108 tracing::trace!(
109 "shift Label target with offset={offset}: 0x{target:x}(0x{ptr:x}) -> 0x{:x}",
110 next_target,
111 );
112 *target = next_target;
113 }
114 }
115 Ok(())
116 }
117}