zingen/jump/
relocate.rs

1//! Program Relocations
2//!
3//! This module provides functionality for relocating program counters associated
4//! with various jump types in the jump table. It handles the adjustment of
5//! program counters based on their target locations, ensuring that jumps
6//! point to the correct addresses after any modifications to the code section.
7
8use crate::{
9    jump::{relocate, JumpTable},
10    wasm::ToLSBytes,
11    Buffer, Error, Result, BUFFER_LIMIT,
12};
13use opcodes::ShangHai as OpCode;
14
15impl JumpTable {
16    /// Relocate program counter to all registered labels.
17    ///
18    /// This function is responsible for adjusting the program counters of all
19    /// jumps in the jump table. It first pre-calculates and shifts the target
20    /// program counters, then iterates through each jump to relocate its
21    /// target address. The function ensures that the jumps are correctly
22    /// updated in the buffer, which represents the compiled code.
23    ///
24    /// *WARNING*: This function should only be called once in the compiler.
25    /// Consider moving it to the compiler's main logic.
26    pub fn relocate(&mut self, buffer: &mut Buffer) -> Result<()> {
27        // Pre-calculate and shift targets to ensure all jumps point to the correct addresses.
28        self.shift_targets()?;
29        tracing::trace!("code section offset: 0x{:x}", self.code.offset());
30
31        // Relocate each function in the jump table.
32        while let Some((pc, jump)) = self.jump.pop_first() {
33            tracing::debug!(
34                "Relocating jump {:?} at pc=0x{:x}, current_offset=0x{:x}",
35                jump,
36                pc,
37                self.code.offset()
38            );
39            let target = self.target(&jump)?;
40
41            tracing::debug!(
42                "relocate: pc=0x{:x}, jump={:?}, target=0x{:x}",
43                pc,
44                jump,
45                target
46            );
47
48            // Update the buffer with the new target program counter.
49            let offset = relocate::pc(buffer, pc, target)?;
50            self.shift_label_pc(pc, offset as u16)?;
51        }
52
53        // Extend the buffer with the finished code section.
54        buffer.extend_from_slice(&self.code.finish());
55        Ok(())
56    }
57}
58
59/// Relocate program counter to buffer.
60///
61/// This function takes the original program counter and the target program
62/// counter, and updates the provided buffer with the necessary opcode
63/// instructions. It ensures that the buffer does not exceed the defined
64/// size limit and handles the conversion of the target program counter
65/// to the appropriate byte representation.
66fn pc(buffer: &mut Buffer, original_pc: u16, target_pc: u16) -> Result<usize> {
67    let original_pc = original_pc as usize;
68    let mut new_buffer: Buffer = buffer[..original_pc].into();
69    let rest_buffer: Buffer = buffer[original_pc..].into();
70
71    // Convert the target program counter to its byte representation.
72    let target = target_pc.to_ls_bytes();
73    if target.len() == 1 {
74        new_buffer.push(OpCode::PUSH1.into());
75    } else {
76        new_buffer.push(OpCode::PUSH2.into());
77    }
78
79    tracing::trace!(
80        "push bytes: 0x{} at 0x{}",
81        hex::encode(&target),
82        hex::encode(original_pc.to_ls_bytes())
83    );
84    new_buffer.extend_from_slice(&target);
85    new_buffer.extend_from_slice(&rest_buffer);
86
87    // Check if the new buffer size exceeds the defined limit.
88    if new_buffer.len() > BUFFER_LIMIT {
89        return Err(Error::BufferOverflow(new_buffer.len()));
90    }
91
92    // Update the original buffer with the new contents.
93    *buffer = new_buffer;
94    Ok(1 + target.len())
95}