// rasm-core/src/codegen/reloc.rs
//
//! # Таблица релокаций
//!
//! ## Проблема
//!
//! При кодировании инструкций мы обрабатываем их **по порядку**, слева направо.
//! Когда встречается `jmp .end` — мы ещё не знаем, по какому байтовому адресу будет `.end`.
//! Нам нужно:
//! 1. Записать в буфер заглушку (`0x00000000`) на месте адреса.
//! 2. Запомнить, где эта заглушка и для какой метки она.
//! 3. После кодирования **всех** инструкций пройти по таблице релокаций
//!    и прошить реальные адреса вместо заглушек.
//!
//! Этот процесс называется **релокацией** (relocation / fixup).
//!
//! ## Виды адресации в x86_64
//!
//! ### Относительная (RIP-relative) — используется в JMP, JE, CALL и т.д.
//!
//! ```text
//! jmp target   ->   E9 <rel32>
//! ```
//!
//! `rel32` — это **смещение относительно следующей инструкции**:
//! ```text
//! rel32 = target_offset - (patch_offset + 4)
//! ```
//! где `patch_offset` — байтовый адрес заглушки в буфере,
//! а `+4` — размер самого поля rel32.
//!
//! ### Абсолютная — используется в данных (`.quad my_label`)
//!
//! ```text
//! absolute = base_address + target_offset
//! ```
//! Для JIT это адрес в исполняемой памяти.
//! Для AOT это записывается в таблицу релокаций объектного файла.
use crate::str_pool::StrId;

/// Вид релокации — как именно нужно прошить адрес.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelocKind {
    /// Относительный 32-битный адрес (RIP-relative).
    /// Используется для `jmp`, `je`, `jne`, `call` и других инструкций ветвления.
    /// Формула: `target_byte_offset - (patch_offset + 4)`
    Rel32,

    /// Абсолютный 64-битный адрес.
    /// Используется для `.quad my_label` в секции данных.
    Abs64,
}

/// Одна запись в таблице релокаций.
#[derive(Debug, Clone)]
pub struct Relocation {
    /// Смещение **в байтовом буфере**, где стоит заглушка (4 нулевых байта).
    pub patch_offset: usize,

    /// Имя метки (StrId из пула строк), адрес которой нужно прошить.
    pub target_label: StrId,

    /// Вид релокации — как считать и записать адрес.
    pub kind: RelocKind,
}

impl Relocation {
    pub fn new(patch_offset: usize, target_label: StrId, kind: RelocKind) -> Self {
        Self {
            patch_offset,
            target_label,
            kind,
        }
    }
}

/// Таблица релокаций — список всех "дырок", которые нужно залатать после кодирования.
#[derive(Debug, Default)]
pub struct RelocTable {
    pub entries: Vec<Relocation>,
}

impl RelocTable {
    pub fn new() -> Self {
        Self {
            entries: Vec::new(),
        }
    }

    /// Добавляет новую запись о релокации.
    pub fn add(&mut self, patch_offset: usize, target_label: StrId, kind: RelocKind) {
        self.entries
            .push(Relocation::new(patch_offset, target_label, kind));
    }
}
