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