Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+634
View File
@@ -0,0 +1,634 @@
// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module arm64
// ARM64 Instruction Encoding Helpers
// These functions provide type-safe instruction encoding for the ARM64 backend.
// Register type for type safety
type Reg = int
// Common register constants
const xzr = Reg(31) // Zero register
const sp = Reg(31) // Stack pointer (context-dependent)
const fp = Reg(29) // Frame pointer
const lr = Reg(30) // Link register
// === Prologue/Epilogue ===
// stp fp, lr, [sp, -16]! (pre-indexed)
fn asm_stp_fp_lr_pre() u32 {
return 0xA9BF7BFD
}
// mov fp, sp
fn asm_mov_fp_sp() u32 {
return 0x910003FD
}
// ldp fp, lr, [sp], 16 (post-indexed)
fn asm_ldp_fp_lr_post() u32 {
return 0xA8C17BFD
}
// ret
fn asm_ret() u32 {
return 0xD65F03C0
}
// === Store/Load Pair ===
// stp r1, r2, [sp, -16]! (callee-saved pair)
fn asm_stp_pair_pre(r1 Reg, r2 Reg) u32 {
return 0xA9BF0000 | (u32(r2) << 10) | (31 << 5) | u32(r1)
}
// ldp r1, r2, [sp], 16 (restore callee-saved pair)
fn asm_ldp_pair_post(r1 Reg, r2 Reg) u32 {
return 0xA8C10000 | (u32(r2) << 10) | (31 << 5) | u32(r1)
}
// === Arithmetic ===
// add rd, rn, #imm12
fn asm_add_imm(rd Reg, rn Reg, imm u32) u32 {
return 0x91000000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
}
// add rd, rn, #imm, lsl #12
fn asm_add_imm_lsl12(rd Reg, rn Reg, imm u32) u32 {
return 0x91400000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
}
// add rd, rn, rm
fn asm_add_reg(rd Reg, rn Reg, rm Reg) u32 {
return 0x8B000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// add rd, rn, rm, lsl #3 (for GEP: index * 8)
fn asm_add_reg_lsl3(rd Reg, rn Reg, rm Reg) u32 {
return 0x8B000C00 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// sub rd, rn, #imm12
fn asm_sub_imm(rd Reg, rn Reg, imm u32) u32 {
return 0xD1000000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
}
// sub rd, rn, #imm12, lsl #12
fn asm_sub_imm_lsl12(rd Reg, rn Reg, imm u32) u32 {
return 0xD1400000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
}
// sub rd, rn, rm
fn asm_sub_reg(rd Reg, rn Reg, rm Reg) u32 {
return 0xCB000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// madd rd, rn, rm, xzr (multiply: rd = rn * rm)
fn asm_mul(rd Reg, rn Reg, rm Reg) u32 {
return 0x9B007C00 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// sdiv rd, rn, rm
fn asm_sdiv(rd Reg, rn Reg, rm Reg) u32 {
return 0x9AC00C00 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// udiv rd, rn, rm
fn asm_udiv(rd Reg, rn Reg, rm Reg) u32 {
return 0x9AC00800 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// msub rd, rn, rm, ra (rd = ra - rn * rm)
fn asm_msub(rd Reg, rn Reg, rm Reg, ra Reg) u32 {
return 0x9B008000 | (u32(rm) << 16) | (u32(ra) << 10) | (u32(rn) << 5) | u32(rd)
}
// === Logical ===
// and rd, rn, rm
fn asm_and(rd Reg, rn Reg, rm Reg) u32 {
return 0x8A000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// and rd, rn, #1 — truncate to 1 bit (AND Xd, Xn, #1: N=1, immr=0, imms=0x00)
fn asm_and_imm_1(rd Reg, rn Reg) u32 {
return 0x92400000 | (u32(rn) << 5) | u32(rd)
}
// and rd, rn, #0xFF — zero-extend u8 (AND Xd, Xn, #0xFF: N=1, immr=0, imms=0x07)
fn asm_and_imm_0xff(rd Reg, rn Reg) u32 {
return 0x92401C00 | (u32(rn) << 5) | u32(rd)
}
// and rd, rn, #0xFFFF — zero-extend u16 (AND Xd, Xn, #0xFFFF: N=1, immr=0, imms=0x0F)
fn asm_and_imm_0xffff(rd Reg, rn Reg) u32 {
return 0x92403C00 | (u32(rn) << 5) | u32(rd)
}
// and rd, rn, #0xFFFFFFFF — zero-extend u32 (AND Xd, Xn, #0xFFFFFFFF: N=1, immr=0, imms=0x1F)
fn asm_and_imm_0xffffffff(rd Reg, rn Reg) u32 {
return 0x92407C00 | (u32(rn) << 5) | u32(rd)
}
// orr rd, rn, rm
fn asm_orr(rd Reg, rn Reg, rm Reg) u32 {
return 0xAA000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// eor rd, rn, rm (xor)
fn asm_eor(rd Reg, rn Reg, rm Reg) u32 {
return 0xCA000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// orr rd, xzr, rm (mov rd, rm)
fn asm_mov_reg(rd Reg, rm Reg) u32 {
return 0xAA0003E0 | (u32(rm) << 16) | u32(rd)
}
// === Shifts ===
// lslv rd, rn, rm (logical shift left variable)
fn asm_lslv(rd Reg, rn Reg, rm Reg) u32 {
return 0x9AC02000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// asrv rd, rn, rm (arithmetic shift right variable)
fn asm_asrv(rd Reg, rn Reg, rm Reg) u32 {
return 0x9AC02800 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// lsrv rd, rn, rm (logical shift right variable)
fn asm_lsrv(rd Reg, rn Reg, rm Reg) u32 {
return 0x9AC02400 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
}
// lsr xd, xn, #shift (logical shift right immediate, 64-bit)
// Alias for UBFM Xd, Xn, #shift, #63
fn asm_lsr_imm(rd Reg, rn Reg, shift u32) u32 {
return 0xD340FC00 | ((shift & 0x3F) << 16) | (u32(rn) << 5) | u32(rd)
}
// ubfx xd, xn, #0, #width (unsigned bitfield extract, zero-extend lower bits)
// Alias for UBFM Xd, Xn, #0, #(width-1)
fn asm_ubfx_lower(rd Reg, rn Reg, width u32) u32 {
imms := (width - 1) & 0x3F
return 0xD3400000 | (imms << 10) | (u32(rn) << 5) | u32(rd)
}
// sxtw rd, rn — sign-extend 32-bit value to 64-bit (SBFM Xd, Xn, #0, #31)
fn asm_sxtw(rd Reg, rn Reg) u32 {
return 0x93407C00 | (u32(rn) << 5) | u32(rd)
}
// sxth rd, rn — sign-extend 16-bit value to 64-bit (SBFM Xd, Xn, #0, #15)
fn asm_sxth(rd Reg, rn Reg) u32 {
return 0x93403C00 | (u32(rn) << 5) | u32(rd)
}
// sxtb rd, rn — sign-extend 8-bit value to 64-bit (SBFM Xd, Xn, #0, #7)
fn asm_sxtb(rd Reg, rn Reg) u32 {
return 0x93401C00 | (u32(rn) << 5) | u32(rd)
}
// === Compare ===
// cmp rn, rm (subs xzr, rn, rm) — 64-bit
fn asm_cmp_reg(rn Reg, rm Reg) u32 {
return 0xEB00001F | (u32(rm) << 16) | (u32(rn) << 5)
}
// cmp xn, #imm12 (subs xzr, xn, #imm12) — 64-bit immediate compare
fn asm_cmp_imm(rn Reg, imm12 u32) u32 {
return 0xF100001F | (imm12 << 10) | (u32(rn) << 5)
}
// cmp wn, #imm12 (subs wzr, wn, #imm12) — 32-bit immediate compare
fn asm_cmp_imm_w(rn Reg, imm12 u32) u32 {
return 0x7100001F | (imm12 << 10) | (u32(rn) << 5)
}
// cmp wn, wm (subs wzr, wn, wm) — 32-bit, sign-aware for i32
fn asm_cmp_reg_w(rn Reg, rm Reg) u32 {
return 0x6B00001F | (u32(rm) << 16) | (u32(rn) << 5)
}
// === Conditional Set ===
// cset rd, eq
fn asm_cset_eq(rd Reg) u32 {
return 0x9A9F17E0 | u32(rd)
}
// cset rd, ne
fn asm_cset_ne(rd Reg) u32 {
return 0x9A9F07E0 | u32(rd)
}
// cset rd, lt
fn asm_cset_lt(rd Reg) u32 {
return 0x9A9FA7E0 | u32(rd)
}
// cset rd, gt
fn asm_cset_gt(rd Reg) u32 {
return 0x9A9FD7E0 | u32(rd)
}
// cset rd, le
fn asm_cset_le(rd Reg) u32 {
return 0x9A9FC7E0 | u32(rd)
}
// cset rd, ge
fn asm_cset_ge(rd Reg) u32 {
return 0x9A9FB7E0 | u32(rd)
}
// cset rd, hi (unsigned greater than)
fn asm_cset_hi(rd Reg) u32 {
return 0x9A9F97E0 | u32(rd)
}
// cset rd, hs (unsigned greater or equal)
fn asm_cset_hs(rd Reg) u32 {
return 0x9A9F37E0 | u32(rd)
}
// cset rd, lo (unsigned less than)
fn asm_cset_lo(rd Reg) u32 {
return 0x9A9F27E0 | u32(rd)
}
// cset rd, ls (unsigned less or equal)
fn asm_cset_ls(rd Reg) u32 {
return 0x9A9F87E0 | u32(rd)
}
// === Float Compare ===
// fcmp dn, dm (compare two double-precision floats, sets NZCV)
fn asm_fcmp_d(dn Reg, dm Reg) u32 {
return 0x1E602000 | (u32(dm) << 16) | (u32(dn) << 5)
}
// === Memory ===
// str rt, [rn] (store 64-bit)
fn asm_str(rt Reg, rn Reg) u32 {
return 0xF9000000 | (u32(rn) << 5) | u32(rt)
}
// str wt, [rn] (store 32-bit)
fn asm_str_w(rt Reg, rn Reg) u32 {
return 0xB9000000 | (u32(rn) << 5) | u32(rt)
}
// strh wt, [rn] (store 16-bit)
fn asm_str_h(rt Reg, rn Reg) u32 {
return 0x79000000 | (u32(rn) << 5) | u32(rt)
}
// strb wt, [rn] (store 8-bit)
fn asm_str_b(rt Reg, rn Reg) u32 {
return 0x39000000 | (u32(rn) << 5) | u32(rt)
}
// str rt, [rn, #imm12] (scaled by 8)
// imm12 is the byte offset divided by 8; range 0..32760 step 8
fn asm_str_imm(rt Reg, rn Reg, imm12 u32) u32 {
return 0xF9000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// str wt, [rn, #imm12] (32-bit store, unsigned offset scaled by 4)
// imm12 is the byte offset divided by 4; range 0..16380 step 4
fn asm_str_imm_w(rt Reg, rn Reg, imm12 u32) u32 {
return 0xB9000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// strh wt, [rn, #imm12] (16-bit store, unsigned offset scaled by 2)
// imm12 is the byte offset divided by 2; range 0..8190 step 2
fn asm_str_imm_h(rt Reg, rn Reg, imm12 u32) u32 {
return 0x79000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// strb wt, [rn, #imm12] (8-bit store, unsigned offset)
// imm12 is the byte offset; range 0..4095
fn asm_str_imm_b(rt Reg, rn Reg, imm12 u32) u32 {
return 0x39000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// stp rt, rt2, [rn, #simm7] (store pair, signed offset scaled by 8)
// simm7 is byte offset / 8; range -512..504 step 8
fn asm_stp_offset(rt Reg, rt2 Reg, rn Reg, simm7 i32) u32 {
return 0xA9000000 | (u32(simm7 & 0x7F) << 15) | (u32(rt2) << 10) | (u32(rn) << 5) | u32(rt)
}
// ldp rt, rt2, [rn, #simm7] (load pair, signed offset scaled by 8)
// simm7 is byte offset / 8; range -512..504 step 8
fn asm_ldp_offset(rt Reg, rt2 Reg, rn Reg, simm7 i32) u32 {
return 0xA9400000 | (u32(simm7 & 0x7F) << 15) | (u32(rt2) << 10) | (u32(rn) << 5) | u32(rt)
}
// stur xt, [xn, #simm9] (unscaled 64-bit store)
fn asm_stur(rt Reg, rn Reg, simm9 i32) u32 {
return 0xF8000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// stur wt, [xn, #simm9] (unscaled 32-bit store)
fn asm_stur_w(rt Reg, rn Reg, simm9 i32) u32 {
return 0xB8000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// sturh wt, [xn, #simm9] (unscaled 16-bit store)
fn asm_stur_h(rt Reg, rn Reg, simm9 i32) u32 {
return 0x78000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// sturb wt, [xn, #simm9] (unscaled 8-bit store)
fn asm_stur_b(rt Reg, rn Reg, simm9 i32) u32 {
return 0x38000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// ldr rt, [rn] (load 64-bit)
fn asm_ldr(rt Reg, rn Reg) u32 {
return 0xF9400000 | (u32(rn) << 5) | u32(rt)
}
// ldr wt, [rn] (load 32-bit, zero-extend to x)
fn asm_ldr_w(rt Reg, rn Reg) u32 {
return 0xB9400000 | (u32(rn) << 5) | u32(rt)
}
// ldrsw xt, [rn] (load 32-bit, sign-extend to 64-bit x)
// Used for signed i32 loads to preserve negative values in 64-bit registers.
fn asm_ldrsw(rt Reg, rn Reg) u32 {
return 0xB9800000 | (u32(rn) << 5) | u32(rt)
}
// ldrh wt, [rn] (load 16-bit, zero-extend to x)
fn asm_ldr_h(rt Reg, rn Reg) u32 {
return 0x79400000 | (u32(rn) << 5) | u32(rt)
}
// ldrb wt, [rn] (load 8-bit, zero-extend to x)
fn asm_ldr_b(rt Reg, rn Reg) u32 {
return 0x39400000 | (u32(rn) << 5) | u32(rt)
}
// ldr rt, [rn, #imm12] (load 64-bit with unsigned scaled offset)
// imm12 is the byte offset divided by 8; range 0..32760 step 8
fn asm_ldr_imm(rt Reg, rn Reg, imm12 u32) u32 {
return 0xF9400000 | ((imm12 & 0xFFF) << 10) | (u32(rn) << 5) | u32(rt)
}
// ldr wt, [rn, #imm12] (32-bit load, unsigned offset scaled by 4)
// imm12 is the byte offset divided by 4; range 0..16380 step 4
fn asm_ldr_imm_w(rt Reg, rn Reg, imm12 u32) u32 {
return 0xB9400000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// ldrh wt, [rn, #imm12] (16-bit load, unsigned offset scaled by 2)
// imm12 is the byte offset divided by 2; range 0..8190 step 2
fn asm_ldr_imm_h(rt Reg, rn Reg, imm12 u32) u32 {
return 0x79400000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// ldrb wt, [rn, #imm12] (8-bit load, unsigned offset)
// imm12 is the byte offset; range 0..4095
fn asm_ldr_imm_b(rt Reg, rn Reg, imm12 u32) u32 {
return 0x39400000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// ldrsw xt, [rn, #imm12] (32-bit sign-extending load, unsigned offset scaled by 4)
fn asm_ldrsw_imm(rt Reg, rn Reg, imm12 u32) u32 {
return 0xB9800000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
}
// ldur xt, [xn, #simm9] (unscaled 64-bit load)
fn asm_ldur(rt Reg, rn Reg, simm9 i32) u32 {
return 0xF8400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// ldur wt, [xn, #simm9] (unscaled 32-bit load, zero-extend)
fn asm_ldur_w(rt Reg, rn Reg, simm9 i32) u32 {
return 0xB8400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// ldurh wt, [xn, #simm9] (unscaled 16-bit load, zero-extend)
fn asm_ldur_h(rt Reg, rn Reg, simm9 i32) u32 {
return 0x78400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// ldurb wt, [xn, #simm9] (unscaled 8-bit load, zero-extend)
fn asm_ldur_b(rt Reg, rn Reg, simm9 i32) u32 {
return 0x38400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
}
// === Branches ===
// b #imm26 (unconditional branch)
fn asm_b(rel26 i32) u32 {
return 0x14000000 | (u32(rel26) & 0x3FFFFFF)
}
// bl #imm26 (branch with link / call)
fn asm_bl(rel26 i32) u32 {
return 0x94000000 | (u32(rel26) & 0x3FFFFFF)
}
// bl (placeholder for relocation)
fn asm_bl_reloc() u32 {
return 0x94000000
}
// blr rn (branch with link to register)
fn asm_blr(rn Reg) u32 {
return 0xD63F0000 | (u32(rn) << 5)
}
// cbnz rt, #imm19
fn asm_cbnz(rt Reg, rel19 i32) u32 {
return 0xB5000000 | ((u32(rel19) & 0x7FFFF) << 5) | u32(rt)
}
// cbz rt, #imm19
fn asm_cbz(rt Reg, rel19 i32) u32 {
return 0xB4000000 | ((u32(rel19) & 0x7FFFF) << 5) | u32(rt)
}
// b.cond #imm19 (conditional branch)
fn asm_b_cond(cond u32, rel19 i32) u32 {
return 0x54000000 | ((u32(rel19) & 0x7FFFF) << 5) | cond
}
// Condition codes for b.cond
const cond_eq = u32(0x0) // equal
const cond_ne = u32(0x1) // not equal
const cond_lt = u32(0xB) // less than (signed)
const cond_gt = u32(0xC) // greater than (signed)
const cond_le = u32(0xD) // less or equal (signed)
const cond_ge = u32(0xA) // greater or equal (signed)
// === Move Immediate ===
// movz rd, #imm16 (move wide with zero)
fn asm_movz(rd Reg, imm16 u32) u32 {
return 0xD2800000 | (imm16 << 5) | u32(rd)
}
// movk rd, #imm16, lsl #(hw*16) (move wide with keep, hw=0..3)
fn asm_movk(rd Reg, imm16 u32, hw int) u32 {
return 0xF2800000 | (u32(hw) << 21) | (imm16 << 5) | u32(rd)
}
// movn rd, #imm16 (move wide with NOT)
fn asm_movn(rd Reg, imm16 u32) u32 {
return 0x92800000 | (imm16 << 5) | u32(rd)
}
// === PC-Relative Addressing ===
// adrp rd, #imm21 (load page address - placeholder for reloc)
fn asm_adrp(rd Reg) u32 {
return 0x90000000 | u32(rd)
}
// add rd, rd, #imm12 (add page offset - typically follows adrp)
fn asm_add_pageoff(rd Reg) u32 {
return 0x91000000 | u32(rd) | (u32(rd) << 5)
}
// ldr rd, [rd, #imm12] (load from page offset - for GOT access, typically follows adrp)
fn asm_ldr_pageoff(rd Reg) u32 {
return 0xF9400000 | u32(rd) | (u32(rd) << 5)
}
// === Stack Operations ===
// sub sp, sp, Xrm (extended register form: UXTX treats reg 31 as SP)
fn asm_sub_sp_reg(rm Reg) u32 {
return 0xCB200000 | (u32(rm) << 16) | (3 << 13) | (31 << 5) | 31
}
// add sp, sp, Xrm (extended register form: UXTX treats reg 31 as SP)
fn asm_add_sp_reg(rm Reg) u32 {
return 0x8B200000 | (u32(rm) << 16) | (3 << 13) | (31 << 5) | 31
}
// sub x10, x29, x10
fn asm_sub_fp_to_reg(rd Reg, rm Reg) u32 {
return 0xCB0003A0 | (u32(rm) << 16) | u32(rd)
}
// === Float Operations ===
// fadd dd, dn, dm
fn asm_fadd_d(dd int, dn int, dm int) u32 {
return 0x1E602800 | (u32(dm) << 16) | (u32(dn) << 5) | u32(dd)
}
// fadd d0, d0, d1 (common case)
fn asm_fadd_d0_d0_d1() u32 {
return 0x1E612800
}
// fsub d0, d0, d1
fn asm_fsub_d0_d0_d1() u32 {
return 0x1E613800
}
// fmul d0, d0, d1
fn asm_fmul_d0_d0_d1() u32 {
return 0x1E610800
}
// fdiv d0, d0, d1
fn asm_fdiv_d0_d0_d1() u32 {
return 0x1E611800
}
// fdiv d2, d0, d1
fn asm_fdiv_d2_d0_d1() u32 {
return 0x1E611802
}
// frintz d2, d2 (truncate to integer)
fn asm_frintz_d2() u32 {
return 0x1E65C042
}
// fmsub d0, d2, d1, d0 (d0 = d0 - d2*d1) — used for fmod: a - trunc(a/b)*b
fn asm_fmsub_d0_d2_d1_d0() u32 {
return 0x1F418040
}
// === Float Conversions ===
// fmov xd, dn (copy float bits to integer)
fn asm_fmov_x_d(xd Reg, dn int) u32 {
return 0x9E660000 | (u32(dn) << 5) | u32(xd)
}
// fmov dd, xn (copy integer bits to float)
fn asm_fmov_d_x(dd int, xn Reg) u32 {
return 0x9E670000 | (u32(xn) << 5) | u32(dd)
}
// fcvtzs xd, dn (float to signed int, truncate toward zero)
fn asm_fcvtzs_x_d(xd Reg, dn int) u32 {
return 0x9E780000 | (u32(dn) << 5) | u32(xd)
}
// scvtf dd, xn (signed int to float)
fn asm_scvtf_d_x(dd int, xn Reg) u32 {
return 0x9E620000 | (u32(xn) << 5) | u32(dd)
}
// ucvtf dd, xn (unsigned int to float)
fn asm_ucvtf_d_x(dd int, xn Reg) u32 {
return 0x9E630000 | (u32(xn) << 5) | u32(dd)
}
// fcvtzu xd, dn (float to unsigned int, truncate toward zero)
fn asm_fcvtzu_x_d(xd Reg, dn int) u32 {
return 0x9E790000 | (u32(dn) << 5) | u32(xd)
}
// fmov sd, wn (copy 32-bit integer to single-precision float register)
fn asm_fmov_s_w(sd int, wn Reg) u32 {
return 0x1E270000 | (u32(wn) << 5) | u32(sd)
}
// fmov wd, sn (copy single-precision float to 32-bit integer register)
fn asm_fmov_w_s(wd Reg, sn int) u32 {
return 0x1E260000 | (u32(sn) << 5) | u32(wd)
}
// fcvt dd, sn (convert single-precision to double-precision float)
fn asm_fcvt_d_s(dd int, sn int) u32 {
return 0x1E22C000 | (u32(sn) << 5) | u32(dd)
}
// fcvt sd, dn (convert double-precision to single-precision float)
fn asm_fcvt_s_d(sd int, dn int) u32 {
return 0x1E624000 | (u32(dn) << 5) | u32(sd)
}
// === Special ===
// udf #0 (undefined - trap)
fn asm_udf() u32 {
return 0x00000000
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+297
View File
@@ -0,0 +1,297 @@
// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module arm64
// Mach-O Constants for ARM64
const mh_magic_64 = u32(0xfeedfacf)
const cpu_type_arm64 = 0x0100000c
const cpu_subtype_arm64_all = 0
const lc_segment_64 = 0x19
const lc_symtab = 0x2
const arm64_reloc_branch26 = 2
const arm64_reloc_page21 = 3
const arm64_reloc_pageoff12 = 4
const arm64_reloc_got_load_page21 = 5
const arm64_reloc_got_load_pageoff12 = 6
pub struct MachOObject {
pub mut:
text_data []u8
str_data []u8
data_data []u8
relocs []RelocationInfo
symbols []Symbol
str_table []u8
sym_by_name map[string]int // symbol name → index in symbols
}
struct RelocationInfo {
addr int
sym_idx int
pcrel bool
length int
extern bool
type_ int
}
struct Symbol {
mut:
name string
type_ u8
sect u8
desc u16
value u64
name_off int
}
pub fn MachOObject.new() &MachOObject {
mut m := &MachOObject{
text_data: []u8{}
str_data: []u8{}
data_data: []u8{}
relocs: []RelocationInfo{}
symbols: []Symbol{}
str_table: [u8(0)]
sym_by_name: map[string]int{}
}
return m
}
pub fn (mut m MachOObject) add_symbol(name string, addr u64, is_ext bool, sect u8) int {
typ := if is_ext { u8(0x0f) } else { u8(0x0e) } // N_SECT | N_EXT : N_SECT
// Check if symbol already exists (e.g., was added as undefined earlier)
if i := m.sym_by_name[name] {
mut s := &m.symbols[i]
s.type_ = typ
s.sect = sect
s.value = addr
return i
}
// Add new symbol
idx := m.symbols.len
name_off := m.str_table.len
unsafe { m.str_table.push_many(name.str, name.len) }
m.str_table << 0
m.symbols << Symbol{
name: name
type_: typ
sect: sect
desc: 0
value: addr
name_off: name_off
}
m.sym_by_name[name] = idx
return idx
}
pub fn (mut m MachOObject) add_undefined(name string) int {
// Check for any existing symbol with this name (defined or undefined)
if i := m.sym_by_name[name] {
return i
}
idx := m.symbols.len
name_off := m.str_table.len
unsafe { m.str_table.push_many(name.str, name.len) }
m.str_table << 0
m.symbols << Symbol{
name: name
type_: 0x01 // N_UNDF | N_EXT
sect: 0
desc: 0
value: 0
name_off: name_off
}
m.sym_by_name[name] = idx
return idx
}
pub fn (mut m MachOObject) add_reloc(addr int, sym_idx int, typ int, pcrel bool) {
m.relocs << RelocationInfo{
addr: addr
sym_idx: sym_idx // 0-based symbol table index
type_: typ
pcrel: pcrel
length: 2
extern: true
}
}
pub fn (mut m MachOObject) write(path string) {
mut buf := []u8{}
n_sects := 3
header_size := 32
seg_cmd_size := 72 + (80 * n_sects)
symtab_cmd_size := 24
load_cmds_size := seg_cmd_size + symtab_cmd_size
text_off := header_size + load_cmds_size
text_len := m.text_data.len
cstring_off := text_off + text_len
cstring_len := m.str_data.len
data_off := cstring_off + cstring_len
data_len := m.data_data.len
reloc_off := data_off + data_len
sym_off := reloc_off + (m.relocs.len * 8)
sym_len := m.symbols.len * 16
str_off := sym_off + sym_len
str_size := m.str_table.len
// 1. Header
write_mh_magic_64(mut buf)
write_u32_le(mut buf, u32(cpu_type_arm64))
write_u32_le(mut buf, u32(cpu_subtype_arm64_all))
write_u32_le(mut buf, 1) // MH_OBJECT
write_u32_le(mut buf, 2) // ncmds
write_u32_le(mut buf, u32(load_cmds_size))
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
// 2. LC_SEGMENT_64
write_u32_le(mut buf, u32(lc_segment_64))
write_u32_le(mut buf, u32(seg_cmd_size))
for _ in 0 .. 16 {
buf << 0
}
write_u64_le(mut buf, 0)
write_u64_le(mut buf, u64(text_len + cstring_len + data_len))
write_u64_le(mut buf, u64(text_off))
write_u64_le(mut buf, u64(text_len + cstring_len + data_len))
write_u32_le(mut buf, 7)
write_u32_le(mut buf, 7)
write_u32_le(mut buf, u32(n_sects))
write_u32_le(mut buf, 0)
// Section 1: __text
write_string_fixed(mut buf, '__text', 16)
write_string_fixed(mut buf, '__TEXT', 16)
write_u64_le(mut buf, 0) // Addr 0
write_u64_le(mut buf, u64(text_len))
write_u32_le(mut buf, u32(text_off))
write_u32_le(mut buf, 4)
write_u32_le(mut buf, u32(reloc_off))
write_u32_le(mut buf, u32(m.relocs.len))
write_u32_le(mut buf, u32(0x80000400))
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
// Section 2: __cstring
write_string_fixed(mut buf, '__cstring', 16)
write_string_fixed(mut buf, '__TEXT', 16)
write_u64_le(mut buf, u64(text_len)) // Addr = text_len
write_u64_le(mut buf, u64(cstring_len))
write_u32_le(mut buf, u32(cstring_off))
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 2) // S_CSTRING_LITERALS
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
// Section 3: __data
write_string_fixed(mut buf, '__data', 16)
write_string_fixed(mut buf, '__DATA', 16)
write_u64_le(mut buf, u64(text_len + cstring_len)) // Addr = text_len + cstring_len
write_u64_le(mut buf, u64(data_len))
write_u32_le(mut buf, u32(data_off))
write_u32_le(mut buf, 3) // align 8
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
write_u32_le(mut buf, 0)
// 3. LC_SYMTAB
write_u32_le(mut buf, u32(lc_symtab))
write_u32_le(mut buf, u32(symtab_cmd_size))
write_u32_le(mut buf, u32(sym_off))
write_u32_le(mut buf, u32(m.symbols.len))
write_u32_le(mut buf, u32(str_off))
write_u32_le(mut buf, u32(str_size))
buf << m.text_data
buf << m.str_data
buf << m.data_data
for r in m.relocs {
write_u32_le(mut buf, u32(r.addr))
mut info := u32(r.sym_idx)
if r.pcrel {
info |= (1 << 24)
}
info |= (u32(r.length) << 25)
if r.extern {
info |= (1 << 27)
}
info |= (u32(r.type_) << 28)
write_u32_le(mut buf, info)
}
for s in m.symbols {
write_u32_le(mut buf, u32(s.name_off))
buf << s.type_
buf << s.sect
write_u16_le(mut buf, s.desc)
write_u64_le(mut buf, s.value)
}
buf << m.str_table
if !write_file_array_raw(path, buf) {
panic('failed to write Mach-O object')
}
}
fn write_u32_le(mut b []u8, v u32) {
b << u8(v)
b << u8(v >> 8)
b << u8(v >> 16)
b << u8(v >> 24)
}
fn write_mh_magic_64(mut b []u8) {
b << u8(0xcf)
b << u8(0xfa)
b << u8(0xed)
b << u8(0xfe)
}
fn write_u64_le(mut b []u8, v u64) {
b << u8(v)
b << u8(v >> 8)
b << u8(v >> 16)
b << u8(v >> 24)
b << u8(v >> 32)
b << u8(v >> 40)
b << u8(v >> 48)
b << u8(v >> 56)
}
fn write_u16_le(mut b []u8, v u16) {
b << u8(v)
b << u8(v >> 8)
}
fn write_string_fixed(mut b []u8, s string, len int) {
mut bytes := s.bytes()
for bytes.len < len {
bytes << 0
}
for i in 0 .. len {
b << bytes[i]
}
}