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}