Files
Odin/core/rexcode/arm32/reloc.odin
Flāvius a4f08f8307 Load rexcode encode/decode tables from committed binary blobs
Each ISA's hand-written ENCODING_TABLE (the single source of truth) now lives
in a per-arch tablegen/ metaprogram that flattens it and serializes committed
binary blobs; the library #loads those into @(rodata) at compile time rather
than compiling a table body. No arch keeps encoding_table.odin or
decoding_tables.odin -- only a generated tables.odin loader and tables/*.bin.

* Two-stage, type-checked pipeline: tablegen Stage A emits human-readable
  generated Odin, which compiles and serializes the blobs in Stage B.
* encode() goes through encoding_forms(m); decoders are unchanged apart from
  x86's flattened 2-D index. Decode tables are byte-identical to the old ones.
* build.lua: a LuaJIT driver for the metaprograms, validations, and tests,
  with cross-platform gating and a clear report.
* Docs refreshed; the obsolete forward-looking plan in cross_arch_design.md
  trimmed to what was actually built.
* Attribution headers added to all rexcode source files; the generators emit
  them so generated files keep them.
2026-06-15 07:43:29 -04:00

66 lines
2.4 KiB
Odin

// rexcode · Brendan Punsky (dotbmp@github), original author
package rexcode_arm32
// =============================================================================
// AArch32 RELOCATIONS
// =============================================================================
//
// AArch32 has many PC-relative forms because both A32 and T32 modes encode
// branches with varying displacement widths:
//
// * A32 B/BL: 24-bit signed << 2 (-32MB..+32MB)
// * A32 BLX imm: 24-bit signed << 2 + H bit (half-word align for arm->thumb)
// * T32 unconditional B: 25-bit signed (S/J1/J2/imm10/imm11 scattered, ±16MB)
// * T32 conditional B: 21-bit signed (cond + S/J1/J2/imm6/imm11 scattered, ±1MB)
// * T16 unconditional B: 12-bit signed << 1 (±2KB)
// * T16 conditional B: 9-bit signed << 1 (±256B)
// * T16 CBZ/CBNZ: 7-bit unsigned << 1 (forward only)
// * ADR/LDR-literal: PC-relative literal pool access
//
// PC for arm32 is always (current_inst_addr + 8) in A32 / (+4) in T32.
// The relocation resolver bakes this in.
Relocation_Type :: enum u8 {
NONE = 0,
// A32 branches
BRANCH_A32_24, // B / BL, 24-bit signed << 2
BLX_A32, // BLX imm, 24-bit signed << 2 + H bit at bit 24
// T32 branches
BRANCH_T32_25, // T32 B unconditional (J1/J2 + imm10 + imm11)
BRANCH_T32_21, // T32 B<cond> (S + cond + imm6 + J1/J2 + imm11)
// T16 branches
BRANCH_T16_11, // T16 B unconditional (signed 11-bit << 1)
BRANCH_T16_8, // T16 B<cond> (signed 8-bit << 1)
BRANCH_T16_CBZ, // T16 CBZ/CBNZ (i + imm5 + Rn)
// T32 low-overhead loops (ARMv8.1-M)
BRANCH_T32_WLS, // WLS / WLSTP imm11
BRANCH_T32_LE, // LE / LETP imm11
// Literal load (ADR / LDR PC-rel)
LDR_LITERAL_A32, // signed 12-bit (U bit + imm12)
LDR_LITERAL_T32, // signed 12-bit (U bit + imm12) Thumb-2
LDR_LITERAL_T16, // unsigned 8-bit << 2 (Thumb-1 PC-rel)
ADR_A32, // ADR encoded as ADD/SUB to PC
ADR_T32,
ADR_T16, // Thumb-1 ADR (imm8 << 2)
// Absolute forms via MOVW + MOVT pair
MOVW_ABS, // imm16 low half
MOVT_ABS, // imm16 high half
}
Relocation :: struct #packed {
offset: u32,
label_id: u32,
addend: i32,
type: Relocation_Type,
size: u8, // 2 (T16) or 4 (A32/T32)
inst_idx: u16,
}
#assert(size_of(Relocation) == 16)