Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,635 @@
|
||||
// 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 #shift (move wide with keep)
|
||||
fn asm_movk(rd Reg, imm16 u32, shift int) u32 {
|
||||
hw := u32(shift / 16)
|
||||
return 0xF2800000 | (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, x10
|
||||
fn asm_sub_sp_reg(rm Reg) u32 {
|
||||
return 0xCB000000 | (u32(rm) << 16) | (31 << 5) | 31
|
||||
}
|
||||
|
||||
// add sp, sp, x10
|
||||
fn asm_add_sp_reg(rm Reg) u32 {
|
||||
return 0x8B000000 | (u32(rm) << 16) | (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
|
||||
}
|
||||
|
||||
// fnmsub d0, d2, d1, d0 (d0 = d0 - d2*d1)
|
||||
fn asm_fnmsub_d0_d2_d1_d0() u32 {
|
||||
return 0x1F618000
|
||||
}
|
||||
|
||||
// === 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
@@ -0,0 +1,285 @@
|
||||
// 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
|
||||
|
||||
// import encoding.binary
|
||||
import os
|
||||
|
||||
// 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{
|
||||
str_table: [u8(0)]
|
||||
}
|
||||
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_u32_le(mut buf, mh_magic_64)
|
||||
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
|
||||
|
||||
os.write_file_array(path, buf) or { panic(err) }
|
||||
}
|
||||
|
||||
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_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]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
println(os.args.len > 0)
|
||||
if os.args.len > 0 {
|
||||
println(os.args[0].len > 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
struct FnType {
|
||||
x int
|
||||
}
|
||||
|
||||
struct Void {}
|
||||
|
||||
type Type = FnType | Void
|
||||
|
||||
struct Fn {
|
||||
name string
|
||||
typ Type
|
||||
}
|
||||
|
||||
fn lookup(methods []&Fn, method_name string) bool {
|
||||
for method in methods {
|
||||
if method.name == method_name {
|
||||
return method.typ is FnType
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
method := &Fn{
|
||||
name: 'write_string'
|
||||
typ: FnType{
|
||||
x: 1
|
||||
}
|
||||
}
|
||||
mut methods := []&Fn{}
|
||||
methods << method
|
||||
println(lookup(methods, 'write_string'))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fn main() {
|
||||
mut matrix := [][]int{len: 4}
|
||||
mut worklist := []int{}
|
||||
worklist << 2
|
||||
idx := worklist.pop()
|
||||
matrix[idx] << 10
|
||||
matrix[idx] << 5
|
||||
mut total := 0
|
||||
for value in matrix[2] {
|
||||
total += value
|
||||
}
|
||||
println(total)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
module main
|
||||
|
||||
struct Entry {
|
||||
name string
|
||||
initial_data []u8
|
||||
size int
|
||||
}
|
||||
|
||||
fn collect(entries []Entry) []u8 {
|
||||
mut out := []u8{}
|
||||
for entry in entries {
|
||||
if entry.initial_data.len > 0 {
|
||||
out << entry.initial_data
|
||||
continue
|
||||
}
|
||||
for _ in 0 .. entry.size {
|
||||
out << u8(0)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fn main() {
|
||||
entries := [
|
||||
Entry{
|
||||
name: 'a'
|
||||
initial_data: [u8(1), 2, 3]
|
||||
size: 0
|
||||
},
|
||||
Entry{
|
||||
name: 'b'
|
||||
initial_data: []u8{}
|
||||
size: 4
|
||||
},
|
||||
Entry{
|
||||
name: 'c'
|
||||
initial_data: [u8(9)]
|
||||
size: 0
|
||||
},
|
||||
]
|
||||
out := collect(entries)
|
||||
println(out.len)
|
||||
println(out[0])
|
||||
println(out[5])
|
||||
println(out[7])
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
type ValueID = int
|
||||
type TypeID = int
|
||||
|
||||
struct Value {
|
||||
id int
|
||||
typ TypeID
|
||||
index int
|
||||
}
|
||||
|
||||
struct Function {
|
||||
id int
|
||||
name string
|
||||
typ TypeID
|
||||
mut:
|
||||
blocks []int
|
||||
params []ValueID
|
||||
}
|
||||
|
||||
struct Module {
|
||||
mut:
|
||||
values []Value
|
||||
funcs []Function
|
||||
}
|
||||
|
||||
fn sum_param_types(mut m Module) int {
|
||||
mut out := 0
|
||||
params := m.funcs[0].params.clone()
|
||||
for i in [0, 1] {
|
||||
if i < params.len {
|
||||
param := params[i]
|
||||
out += int(m.values[param].typ)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut m := Module{}
|
||||
for i := 0; i < 200; i++ {
|
||||
m.values << Value{
|
||||
id: i
|
||||
typ: TypeID(i + 10)
|
||||
index: i
|
||||
}
|
||||
}
|
||||
m.funcs << Function{
|
||||
id: 0
|
||||
name: 'f'
|
||||
typ: TypeID(0)
|
||||
params: [ValueID(134), ValueID(135)]
|
||||
}
|
||||
println(sum_param_types(mut m))
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
fn main() {
|
||||
pairs := [
|
||||
[1, 2]!,
|
||||
[2, 3]!,
|
||||
[1, 0]!,
|
||||
]
|
||||
mut got := []int{}
|
||||
for pair in pairs {
|
||||
got << pair[0]
|
||||
got << pair[1]
|
||||
}
|
||||
println(got)
|
||||
for i := 0; i < pairs.len; i++ {
|
||||
pair := pairs[i]
|
||||
println('${pair[0]},${pair[1]}')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module main
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,14 @@
|
||||
module main
|
||||
|
||||
struct DirLike {
|
||||
mut:
|
||||
d_name [256]u8
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut d := DirLike{}
|
||||
d.d_name[255] = 7
|
||||
assert d.d_name[255] == 7
|
||||
assert d.d_name.len == 256
|
||||
println('ok')
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import v2.ast
|
||||
|
||||
fn main() {
|
||||
file := ast.File{
|
||||
name: @FILE
|
||||
mod: 'main'
|
||||
imports: [
|
||||
ast.ImportStmt{
|
||||
name: 'v2.pref'
|
||||
alias: 'pref'
|
||||
},
|
||||
]
|
||||
stmts: [
|
||||
ast.Stmt(ast.ImportStmt{
|
||||
name: 'v2.pref'
|
||||
alias: 'pref'
|
||||
}),
|
||||
]
|
||||
}
|
||||
flat := ast.flatten_files([file])
|
||||
file_node := flat.nodes[flat.files[0].file_id]
|
||||
imports := flat.file_cursor(0).imports().import_stmts()
|
||||
println(file_node.edge_count)
|
||||
println(imports.len)
|
||||
if imports.len > 0 {
|
||||
println(imports[0].name)
|
||||
println(imports[0].alias)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
module main
|
||||
|
||||
const page_size = 64
|
||||
|
||||
@[direct_array_access]
|
||||
fn fill_hash(data &u8, data_len int, out &u8) {
|
||||
for i in 0 .. 32 {
|
||||
unsafe {
|
||||
out[i] = if i < data_len { data[i] + 1 } else { u8(i) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_pages_shape(data &u8, mut hashes []u8, page_start int, page_end int, code_limit int) {
|
||||
mut hash_buf := [32]u8{}
|
||||
for page := page_start; page < page_end; page++ {
|
||||
start := page * page_size
|
||||
mut end := start + page_size
|
||||
if end > code_limit {
|
||||
end = code_limit
|
||||
}
|
||||
unsafe {
|
||||
fill_hash(data + start, end - start, &hash_buf[0])
|
||||
}
|
||||
hash_offset := page * 32
|
||||
for i in 0 .. 32 {
|
||||
hashes[hash_offset + i] = hash_buf[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut data := []u8{len: 150}
|
||||
for i in 0 .. data.len {
|
||||
data[i] = u8(i % 251)
|
||||
}
|
||||
mut hashes := []u8{len: 96}
|
||||
unsafe {
|
||||
hash_pages_shape(data.data, mut hashes, 0, 3, data.len)
|
||||
}
|
||||
println(hashes[0])
|
||||
println(hashes[32])
|
||||
println(hashes[64])
|
||||
println(hashes[95])
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
module main
|
||||
|
||||
struct GenShape {
|
||||
mut:
|
||||
cache []int
|
||||
stack []bool
|
||||
}
|
||||
|
||||
fn new_gen_shape(n int) &GenShape {
|
||||
return &GenShape{
|
||||
cache: []int{len: n}
|
||||
stack: []bool{len: n}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
g := new_gen_shape(256)
|
||||
mut sum := 0
|
||||
for i := 0; i < g.cache.len; i++ {
|
||||
sum += g.cache[i]
|
||||
}
|
||||
println(sum)
|
||||
println(g.cache.len)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
module main
|
||||
|
||||
fn sum_cache(n int) int {
|
||||
cache := []int{len: n}
|
||||
mut sum := 0
|
||||
for i := 0; i < cache.len; i++ {
|
||||
sum += cache[i]
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println(sum_cache(256))
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
println(2147483647)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
module main
|
||||
|
||||
struct TypeShape {
|
||||
kind int
|
||||
width int
|
||||
elem_type int
|
||||
len int
|
||||
fields []int
|
||||
field_names []string
|
||||
params []int
|
||||
ret_type int
|
||||
is_c_struct bool
|
||||
is_union bool
|
||||
is_unsigned bool
|
||||
}
|
||||
|
||||
fn fixed_array_size(types []TypeShape, typ_id int) int {
|
||||
typ := types[typ_id]
|
||||
if typ.kind == 4 {
|
||||
return typ.len * 4
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
types := [
|
||||
TypeShape{
|
||||
kind: 4
|
||||
elem_type: 1
|
||||
len: 128
|
||||
fields: []int{}
|
||||
params: []int{}
|
||||
},
|
||||
]
|
||||
println(fixed_array_size(types, 0))
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
module main
|
||||
|
||||
enum TypeKindShape {
|
||||
void_t
|
||||
int_t
|
||||
float_t
|
||||
ptr_t
|
||||
array_t
|
||||
struct_t
|
||||
func_t
|
||||
label_t
|
||||
metadata_t
|
||||
}
|
||||
|
||||
struct TypeShape {
|
||||
kind TypeKindShape
|
||||
width int
|
||||
elem_type int
|
||||
len int
|
||||
fields []int
|
||||
field_names []string
|
||||
params []int
|
||||
ret_type int
|
||||
is_c_struct bool
|
||||
is_union bool
|
||||
is_unsigned bool
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut types := []TypeShape{}
|
||||
for i := 0; i < 400; i++ {
|
||||
types << TypeShape{
|
||||
kind: .array_t
|
||||
elem_type: 1
|
||||
len: 64 + i
|
||||
}
|
||||
}
|
||||
println(types[0].len)
|
||||
println(types[63].len)
|
||||
println(types[128].len)
|
||||
println(types[399].len)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
module main
|
||||
|
||||
enum TypeKindShape {
|
||||
void_t
|
||||
int_t
|
||||
float_t
|
||||
ptr_t
|
||||
array_t
|
||||
struct_t
|
||||
func_t
|
||||
label_t
|
||||
metadata_t
|
||||
}
|
||||
|
||||
struct TypeShape {
|
||||
kind TypeKindShape
|
||||
width int
|
||||
elem_type int
|
||||
len int
|
||||
fields []int
|
||||
field_names []string
|
||||
params []int
|
||||
ret_type int
|
||||
is_c_struct bool
|
||||
is_union bool
|
||||
is_unsigned bool
|
||||
}
|
||||
|
||||
struct TypeStoreShape {
|
||||
mut:
|
||||
types []TypeShape
|
||||
cache map[string]int
|
||||
}
|
||||
|
||||
fn (mut ts TypeStoreShape) register(t TypeShape) int {
|
||||
id := ts.types.len
|
||||
ts.types << t
|
||||
return id
|
||||
}
|
||||
|
||||
fn (mut ts TypeStoreShape) get_array(elem int, length int) int {
|
||||
return ts.register(TypeShape{
|
||||
kind: .array_t
|
||||
elem_type: elem
|
||||
len: length
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut ts := TypeStoreShape{}
|
||||
ts.cache = map[string]int{}
|
||||
id := ts.get_array(1, 128)
|
||||
typ := ts.types[id]
|
||||
println(typ.len)
|
||||
println(typ.elem_type)
|
||||
println(int(typ.kind))
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fn push_name(mut names []string, name string) {
|
||||
names << name
|
||||
println(names.len)
|
||||
}
|
||||
|
||||
fn wrap(mut names []string, name string) {
|
||||
push_name(mut names, name)
|
||||
println(names.len)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut names := []string{}
|
||||
wrap(mut names, 'abc')
|
||||
println(names.len)
|
||||
if names.len > 0 {
|
||||
println(names[0])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
mut lists := [][]int{len: 4}
|
||||
for i := 0; i < lists.len; i++ {
|
||||
lists[i] << (i + 10)
|
||||
lists[i] << (i + 20)
|
||||
}
|
||||
mut total := 0
|
||||
for i := 0; i < lists.len; i++ {
|
||||
total += lists[i].len
|
||||
total += lists[i][0]
|
||||
total += lists[i][1]
|
||||
}
|
||||
println(total)
|
||||
println(lists[3][0])
|
||||
println(lists[3][1])
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
module main
|
||||
|
||||
struct EmptyExpr {
|
||||
}
|
||||
|
||||
struct BasicLiteral {
|
||||
value string
|
||||
}
|
||||
|
||||
struct Ident {
|
||||
name string
|
||||
}
|
||||
|
||||
type Type = ArrayFixedType | ArrayType
|
||||
|
||||
type Expr = ArrayInitExpr | BasicLiteral | EmptyExpr | Ident | Type
|
||||
|
||||
struct ArrayType {
|
||||
elem_type Expr
|
||||
}
|
||||
|
||||
struct ArrayFixedType {
|
||||
len Expr
|
||||
elem_type Expr
|
||||
}
|
||||
|
||||
struct ArrayInitExpr {
|
||||
typ Expr
|
||||
exprs []Expr
|
||||
}
|
||||
|
||||
struct FieldDecl {
|
||||
name string
|
||||
value Expr
|
||||
}
|
||||
|
||||
fn parse_digits(s string) int {
|
||||
mut out := 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
out = out * 10 + int(s[i] - `0`)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fn fixed_len_from_type(typ Type) int {
|
||||
return match typ {
|
||||
ArrayFixedType {
|
||||
if typ.len is BasicLiteral {
|
||||
parse_digits(typ.len.value)
|
||||
} else {
|
||||
-2
|
||||
}
|
||||
}
|
||||
ArrayType {
|
||||
-3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_to_len(expr Expr) int {
|
||||
return match expr {
|
||||
Type {
|
||||
fixed_len_from_type(expr)
|
||||
}
|
||||
else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn field_len(field FieldDecl) int {
|
||||
if field.value is ArrayInitExpr {
|
||||
arr := field.value
|
||||
return type_to_len(arr.typ)
|
||||
}
|
||||
return -4
|
||||
}
|
||||
|
||||
fn main() {
|
||||
field := FieldDecl{
|
||||
name: 'buf'
|
||||
value: Expr(ArrayInitExpr{
|
||||
typ: Expr(Type(ArrayFixedType{
|
||||
len: Expr(BasicLiteral{
|
||||
value: '128'
|
||||
})
|
||||
elem_type: Expr(Ident{
|
||||
name: 'u8'
|
||||
})
|
||||
}))
|
||||
exprs: []Expr{}
|
||||
})
|
||||
}
|
||||
println(field_len(field))
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
module main
|
||||
|
||||
struct EmptyExpr {
|
||||
}
|
||||
|
||||
struct BasicLiteral {
|
||||
value int
|
||||
}
|
||||
|
||||
struct Ident {
|
||||
name string
|
||||
}
|
||||
|
||||
type Type = ArrayFixedType | ArrayType
|
||||
|
||||
type Expr = ArrayInitExpr | BasicLiteral | EmptyExpr | Ident | Type
|
||||
|
||||
struct ArrayType {
|
||||
elem_type Expr
|
||||
}
|
||||
|
||||
struct ArrayFixedType {
|
||||
len Expr
|
||||
elem_type Expr
|
||||
}
|
||||
|
||||
struct ArrayInitExpr {
|
||||
typ Expr
|
||||
exprs []Expr
|
||||
}
|
||||
|
||||
struct FieldDecl {
|
||||
name string
|
||||
value Expr
|
||||
}
|
||||
|
||||
fn fixed_len_from_type(typ Type) int {
|
||||
return match typ {
|
||||
ArrayFixedType {
|
||||
if typ.len is BasicLiteral {
|
||||
typ.len.value
|
||||
} else {
|
||||
-2
|
||||
}
|
||||
}
|
||||
ArrayType {
|
||||
-3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_to_len(expr Expr) int {
|
||||
return match expr {
|
||||
Type {
|
||||
fixed_len_from_type(expr)
|
||||
}
|
||||
else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn field_len(field FieldDecl) int {
|
||||
if field.value is ArrayInitExpr {
|
||||
arr := field.value
|
||||
return type_to_len(arr.typ)
|
||||
}
|
||||
return -4
|
||||
}
|
||||
|
||||
fn main() {
|
||||
field := FieldDecl{
|
||||
name: 'buf'
|
||||
value: Expr(ArrayInitExpr{
|
||||
typ: Expr(Type(ArrayFixedType{
|
||||
len: Expr(BasicLiteral{
|
||||
value: 128
|
||||
})
|
||||
elem_type: Expr(Ident{
|
||||
name: 'u8'
|
||||
})
|
||||
}))
|
||||
exprs: []Expr{}
|
||||
})
|
||||
}
|
||||
println(field_len(field))
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import v2.ast
|
||||
|
||||
struct Parameter {
|
||||
typ ast.Expr
|
||||
}
|
||||
|
||||
struct FnType {
|
||||
params []Parameter
|
||||
}
|
||||
|
||||
fn classify(expr ast.Expr) int {
|
||||
match expr {
|
||||
ast.Type {
|
||||
if expr is ast.ArrayType {
|
||||
return 11
|
||||
}
|
||||
return 7
|
||||
}
|
||||
else {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_param_types(fn_type FnType) int {
|
||||
mut out := 0
|
||||
idx := 0
|
||||
first := fn_type.params[idx]
|
||||
out += classify(first.typ)
|
||||
for i := 0; i < fn_type.params.len; i++ {
|
||||
param := fn_type.params[i]
|
||||
out += classify(param.typ)
|
||||
}
|
||||
for _, param in fn_type.params {
|
||||
out += classify(param.typ)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fn main() {
|
||||
typ := ast.Type(ast.ArrayType{
|
||||
elem_type: ast.Expr(ast.Ident{
|
||||
name: 'int'
|
||||
})
|
||||
})
|
||||
fn_type := FnType{
|
||||
params: [
|
||||
Parameter{
|
||||
typ: ast.Expr(typ)
|
||||
},
|
||||
]
|
||||
}
|
||||
println(classify_param_types(fn_type))
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
assert 0o755 == 493
|
||||
assert 0b1010_0101 == 165
|
||||
println('ok')
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
module main
|
||||
|
||||
fn to_i64_direct(x u64) i64 {
|
||||
return i64(x)
|
||||
}
|
||||
|
||||
fn to_i64_if_expr(x u64, neg bool) i64 {
|
||||
return if neg { -i64(x) } else { i64(x) }
|
||||
}
|
||||
|
||||
fn to_i64_result_direct(x u64) !i64 {
|
||||
return i64(x)
|
||||
}
|
||||
|
||||
fn to_i64_result_if_expr(x u64, neg bool) !i64 {
|
||||
return if neg { -i64(x) } else { i64(x) }
|
||||
}
|
||||
|
||||
fn to_i64_result_if_var(x u64, neg bool) !i64 {
|
||||
y := if neg { -i64(x) } else { i64(x) }
|
||||
return y
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println(to_i64_direct(6))
|
||||
println(to_i64_if_expr(6, false))
|
||||
println(to_i64_result_direct(6) or { 99 })
|
||||
println(to_i64_result_if_expr(6, false) or { 99 })
|
||||
println(to_i64_result_if_var(6, false) or { 99 })
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
|
||||
struct Sample {
|
||||
a voidptr
|
||||
b int
|
||||
c bool
|
||||
}
|
||||
|
||||
fn mk(path string) !Sample {
|
||||
cfile := os.vfopen(path, 'wb')!
|
||||
fd := os.fileno(cfile)
|
||||
return Sample{
|
||||
a: cfile
|
||||
b: fd
|
||||
c: true
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s := mk('/tmp/v2_result_postfix_unwrap.txt') or {
|
||||
println('err')
|
||||
return
|
||||
}
|
||||
println(s.b > 0)
|
||||
println(s.c)
|
||||
}
|
||||
|
||||
// v2-clobber-test
|
||||
@@ -0,0 +1,101 @@
|
||||
// Test runner for ARM64 backend tests
|
||||
// Run with: ./v run vlib/v2_toberemoved/gen/arm64/tests/run_tests.v
|
||||
// Or: cd vlib/v2_toberemoved/gen/arm64/tests && v run run_tests.v
|
||||
|
||||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
fn main() {
|
||||
t0 := time.now()
|
||||
|
||||
vroot := os.dir(@VEXE)
|
||||
v2_source := os.join_path(vroot, 'cmd', 'v2', 'v2.v')
|
||||
v2_binary := os.join_path(vroot, 'cmd', 'v2', 'v2')
|
||||
tests_dir := os.join_path(vroot, 'vlib', 'v2', 'gen', 'arm64', 'tests')
|
||||
|
||||
// Build v2 compiler
|
||||
println('[*] Building v2...')
|
||||
build_res := os.execute('${@VEXE} ${v2_source} -o ${v2_binary}')
|
||||
if build_res.exit_code != 0 {
|
||||
eprintln('Error: Failed to build v2')
|
||||
eprintln(build_res.output)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Find all test files (*.v except run_tests.v)
|
||||
test_files := os.ls(tests_dir) or {
|
||||
eprintln('Error: Cannot list tests directory')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
mut passed := 0
|
||||
mut failed := 0
|
||||
mut test_count := 0
|
||||
|
||||
for file in test_files {
|
||||
if !file.ends_with('.v') || file == 'run_tests.v' {
|
||||
continue
|
||||
}
|
||||
test_count++
|
||||
test_path := os.join_path(tests_dir, file)
|
||||
test_name := file.replace('.v', '')
|
||||
|
||||
println('\n[*] Testing: ${file}')
|
||||
|
||||
// Run v2 with ARM64 backend
|
||||
v2_output := os.join_path(tests_dir, 'test_${test_name}')
|
||||
v2_cmd := '${v2_binary} -backend arm64 ${test_path} -o ${v2_output}'
|
||||
v2_res := os.execute(v2_cmd)
|
||||
if v2_res.exit_code != 0 {
|
||||
eprintln(' [FAIL] v2 compilation failed')
|
||||
eprintln(v2_res.output)
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
|
||||
// Run the generated binary
|
||||
gen_res := os.execute(v2_output)
|
||||
if gen_res.exit_code != 0 {
|
||||
eprintln(' [FAIL] Generated binary exited with code ${gen_res.exit_code}')
|
||||
eprintln(gen_res.output)
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
|
||||
// Run reference compiler
|
||||
ref_res := os.execute('${@VEXE} -n -w -enable-globals run ${test_path}')
|
||||
if ref_res.exit_code != 0 {
|
||||
eprintln(' [FAIL] Reference compilation failed')
|
||||
eprintln(ref_res.output)
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
|
||||
// Compare outputs
|
||||
expected := ref_res.output.trim_space().replace('\r\n', '\n')
|
||||
actual := gen_res.output.trim_space().replace('\r\n', '\n')
|
||||
|
||||
if expected == actual {
|
||||
println(' [PASS] Output matches reference')
|
||||
passed++
|
||||
} else {
|
||||
println(' [FAIL] Output mismatch')
|
||||
println(' Expected:\n${expected}')
|
||||
println(' Got:\n${actual}')
|
||||
failed++
|
||||
}
|
||||
|
||||
// Clean up
|
||||
os.rm(v2_output) or {}
|
||||
}
|
||||
|
||||
println('\n========================================')
|
||||
println('Results: ${passed}/${test_count} tests passed')
|
||||
if failed > 0 {
|
||||
println('${failed} tests FAILED')
|
||||
exit(1)
|
||||
}
|
||||
println('Total time: ${time.since(t0)}')
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
println('script main')
|
||||
@@ -0,0 +1,35 @@
|
||||
struct IntInfo {
|
||||
value int
|
||||
}
|
||||
|
||||
struct StructInfo {
|
||||
name string
|
||||
value int
|
||||
}
|
||||
|
||||
type TypeInfo = IntInfo | StructInfo
|
||||
|
||||
fn name_contains_dot(name string) bool {
|
||||
return name.contains('.')
|
||||
}
|
||||
|
||||
fn has_dot(info TypeInfo) bool {
|
||||
mut cur := info
|
||||
if cur is StructInfo {
|
||||
return name_contains_dot(cur.name)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
with_dot := TypeInfo(StructInfo{
|
||||
name: 'foo.bar'
|
||||
value: 1
|
||||
})
|
||||
without_dot := TypeInfo(StructInfo{
|
||||
name: 'foobar'
|
||||
value: 2
|
||||
})
|
||||
println(has_dot(with_dot))
|
||||
println(has_dot(without_dot))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
assert '6'.int() == 6
|
||||
assert '10'.int() == 10
|
||||
assert '13'.int() == 13
|
||||
assert '-1'.int() == -1
|
||||
assert '10'.i32() == i32(10)
|
||||
println('32'.u64())
|
||||
println('32'.i64())
|
||||
println('-32'.i64())
|
||||
println('0x20'.u64())
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Tests for ARM64 backend large struct returns (> 16 bytes)
|
||||
// This tests the fix from commit 9919ddeb5 which handles:
|
||||
// - Saving/restoring x8 (indirect return pointer) for large struct returns
|
||||
// - Proper stack frame layout with callee-saved registers
|
||||
// - Large struct parameter copying
|
||||
// - String operations (string is 24 bytes: ptr + len + is_lit)
|
||||
|
||||
fn print_str(s string) {
|
||||
C.write(1, s.str, s.len)
|
||||
C.write(1, c'\n', 1)
|
||||
}
|
||||
|
||||
// ===================== FUNCTIONS RETURNING STRINGS =====================
|
||||
|
||||
// Test 1: Return a string literal (24 bytes indirect return)
|
||||
fn get_hello() string {
|
||||
return 'hello'
|
||||
}
|
||||
|
||||
// Test 2: Return concatenated string
|
||||
fn get_greeting(name string) string {
|
||||
return 'Hello, ' + name
|
||||
}
|
||||
|
||||
// Test 3: Nested string returns
|
||||
fn get_formal_greeting(name string) string {
|
||||
greeting := get_greeting(name)
|
||||
return greeting + '!'
|
||||
}
|
||||
|
||||
// Test 4: Multiple string concatenations
|
||||
fn concat_three(a string, b string, c string) string {
|
||||
return a + b + c
|
||||
}
|
||||
|
||||
// Test 5: String function calling another string function
|
||||
fn get_full_message() string {
|
||||
greeting := get_hello()
|
||||
suffix := ' world'
|
||||
return greeting + suffix
|
||||
}
|
||||
|
||||
// Test 6: Chain of string returns
|
||||
fn chain_a() string {
|
||||
return 'A'
|
||||
}
|
||||
|
||||
fn chain_b() string {
|
||||
return chain_a() + 'B'
|
||||
}
|
||||
|
||||
fn chain_c() string {
|
||||
return chain_b() + 'C'
|
||||
}
|
||||
|
||||
// Test 7: Conditional string return
|
||||
fn conditional_string(flag bool) string {
|
||||
if flag {
|
||||
return 'true_case'
|
||||
}
|
||||
return 'false_case'
|
||||
}
|
||||
|
||||
// ===================== MAIN =====================
|
||||
|
||||
fn main() {
|
||||
print_str('--- String Return Tests (Large Struct > 16 bytes) ---')
|
||||
|
||||
// Test 1: Basic string literal return
|
||||
s1 := get_hello()
|
||||
print_str(s1)
|
||||
|
||||
// Test 2: String concatenation return
|
||||
s2 := get_greeting('Alice')
|
||||
print_str(s2)
|
||||
|
||||
// Test 3: Nested string function calls
|
||||
s3 := get_formal_greeting('Bob')
|
||||
print_str(s3)
|
||||
|
||||
// Test 4: Multiple parameters concatenation
|
||||
s4 := concat_three('One', '-Two-', 'Three')
|
||||
print_str(s4)
|
||||
|
||||
// Test 5: Function calling function returning strings
|
||||
s5 := get_full_message()
|
||||
print_str(s5)
|
||||
|
||||
// Test 6: Chain of string returns
|
||||
s6 := chain_c()
|
||||
print_str(s6)
|
||||
|
||||
// Test 7: Conditional string returns
|
||||
s7a := conditional_string(true)
|
||||
s7b := conditional_string(false)
|
||||
print_str(s7a)
|
||||
print_str(s7b)
|
||||
|
||||
// Test 8: Direct string operations
|
||||
direct := 'Direct' + ' ' + 'Test'
|
||||
print_str(direct)
|
||||
|
||||
// Test 9: Interpolation with string return
|
||||
name := 'Claude'
|
||||
interp := 'Name: ${name}'
|
||||
print_str(interp)
|
||||
|
||||
// Test 10: Chained string concat expression
|
||||
chained := 'A' + 'B' + 'C' + 'D' + 'E'
|
||||
print_str(chained)
|
||||
|
||||
print_str('--- All tests completed ---')
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module main
|
||||
|
||||
type EmptyExpr = u8
|
||||
|
||||
struct Node {
|
||||
value int
|
||||
}
|
||||
|
||||
type Expr = EmptyExpr | Node
|
||||
|
||||
const empty_expr = Expr(EmptyExpr(0))
|
||||
|
||||
fn main() {
|
||||
if empty_expr is EmptyExpr {
|
||||
println('ok')
|
||||
return
|
||||
}
|
||||
println('bad')
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
module main
|
||||
|
||||
type Type = ArrayFixed | Primitive
|
||||
|
||||
struct Primitive {
|
||||
size int
|
||||
}
|
||||
|
||||
struct ArrayFixed {
|
||||
len int
|
||||
elem_type Type
|
||||
}
|
||||
|
||||
struct ObjectCommon {
|
||||
name string
|
||||
typ Type
|
||||
}
|
||||
|
||||
struct Const {
|
||||
ObjectCommon
|
||||
}
|
||||
|
||||
struct Global {
|
||||
ObjectCommon
|
||||
}
|
||||
|
||||
type Object = Const | Global | Type
|
||||
|
||||
struct Scope {
|
||||
mut:
|
||||
objects map[string]Object
|
||||
}
|
||||
|
||||
fn (s &Scope) lookup(name string) ?Object {
|
||||
if obj := s.objects[name] {
|
||||
return obj
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (obj &Object) typ() Type {
|
||||
match obj {
|
||||
Const {
|
||||
return obj.typ
|
||||
}
|
||||
Global {
|
||||
return obj.typ
|
||||
}
|
||||
Type {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn array_len(t Type) int {
|
||||
return match t {
|
||||
ArrayFixed {
|
||||
t.len
|
||||
}
|
||||
Primitive {
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut s := Scope{
|
||||
objects: map[string]Object{}
|
||||
}
|
||||
s.objects['buf'] = Object(Global{
|
||||
name: 'buf'
|
||||
typ: Type(ArrayFixed{
|
||||
len: 128
|
||||
elem_type: Type(Primitive{
|
||||
size: 8
|
||||
})
|
||||
})
|
||||
})
|
||||
if obj := s.lookup('buf') {
|
||||
println(array_len(obj.typ()))
|
||||
return
|
||||
}
|
||||
println(-2)
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
module main
|
||||
|
||||
enum TypeKindShape {
|
||||
void_t
|
||||
int_t
|
||||
float_t
|
||||
ptr_t
|
||||
array_t
|
||||
struct_t
|
||||
func_t
|
||||
label_t
|
||||
metadata_t
|
||||
}
|
||||
|
||||
struct TypeShape {
|
||||
kind TypeKindShape
|
||||
width int
|
||||
elem_type int
|
||||
len int
|
||||
fields []int
|
||||
field_names []string
|
||||
params []int
|
||||
ret_type int
|
||||
is_c_struct bool
|
||||
is_union bool
|
||||
is_unsigned bool
|
||||
}
|
||||
|
||||
struct TypeStoreShape {
|
||||
mut:
|
||||
types []TypeShape
|
||||
cache map[string]int
|
||||
}
|
||||
|
||||
struct TargetShape {
|
||||
ptr_size int
|
||||
endian_little bool
|
||||
}
|
||||
|
||||
struct ModuleShape {
|
||||
mut:
|
||||
name string
|
||||
target TargetShape
|
||||
ssa_mod &int = unsafe { nil }
|
||||
env &int = unsafe { nil }
|
||||
type_store TypeStoreShape
|
||||
values []int
|
||||
instrs []int
|
||||
blocks []int
|
||||
funcs []int
|
||||
globals []int
|
||||
}
|
||||
|
||||
fn lower(src &ModuleShape) ModuleShape {
|
||||
return ModuleShape{
|
||||
name: src.name
|
||||
target: src.target
|
||||
ssa_mod: src.ssa_mod
|
||||
env: src.env
|
||||
type_store: src.type_store
|
||||
values: src.values.clone()
|
||||
instrs: src.instrs.clone()
|
||||
blocks: src.blocks.clone()
|
||||
funcs: src.funcs.clone()
|
||||
globals: src.globals.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn fixed_array_size(m &ModuleShape, typ_id int) int {
|
||||
typ := m.type_store.types[typ_id]
|
||||
if typ.kind == .array_t {
|
||||
return typ.len * 4
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut src := ModuleShape{}
|
||||
src.name = 'main'
|
||||
src.target = TargetShape{
|
||||
ptr_size: 8
|
||||
endian_little: true
|
||||
}
|
||||
src.type_store.cache = map[string]int{}
|
||||
src.type_store.types = [
|
||||
TypeShape{
|
||||
kind: .array_t
|
||||
elem_type: 1
|
||||
len: 128
|
||||
fields: []int{}
|
||||
params: []int{}
|
||||
},
|
||||
]
|
||||
src.values = [1, 2, 3]
|
||||
src.instrs = [4, 5]
|
||||
src.blocks = [6]
|
||||
src.funcs = [7]
|
||||
src.globals = [8]
|
||||
src.type_store.cache['x'] = 0
|
||||
dst := lower(&src)
|
||||
println(fixed_array_size(&dst, 0))
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
module main
|
||||
|
||||
enum TypeKindShape {
|
||||
void_t
|
||||
int_t
|
||||
float_t
|
||||
ptr_t
|
||||
array_t
|
||||
struct_t
|
||||
func_t
|
||||
label_t
|
||||
metadata_t
|
||||
}
|
||||
|
||||
struct TypeShape {
|
||||
kind TypeKindShape
|
||||
width int
|
||||
elem_type int
|
||||
len int
|
||||
fields []int
|
||||
field_names []string
|
||||
params []int
|
||||
ret_type int
|
||||
is_c_struct bool
|
||||
is_union bool
|
||||
is_unsigned bool
|
||||
}
|
||||
|
||||
struct TypeStoreShape {
|
||||
mut:
|
||||
types []TypeShape
|
||||
cache map[string]int
|
||||
}
|
||||
|
||||
struct ModuleShape {
|
||||
mut:
|
||||
type_store TypeStoreShape
|
||||
}
|
||||
|
||||
fn (mut ts TypeStoreShape) register(t TypeShape) int {
|
||||
id := ts.types.len
|
||||
ts.types << t
|
||||
return id
|
||||
}
|
||||
|
||||
fn (mut ts TypeStoreShape) get_array(elem int, length int) int {
|
||||
key := 'a${elem}_${length}'
|
||||
if id := ts.cache[key] {
|
||||
return id
|
||||
}
|
||||
id := ts.register(TypeShape{
|
||||
kind: .array_t
|
||||
elem_type: elem
|
||||
len: length
|
||||
})
|
||||
ts.cache[key] = id
|
||||
return id
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut ts := TypeStoreShape{
|
||||
cache: map[string]int{}
|
||||
}
|
||||
for i := 0; i < 400; i++ {
|
||||
ts.get_array(1, 64 + i)
|
||||
}
|
||||
src := ModuleShape{
|
||||
type_store: ts
|
||||
}
|
||||
dst := ModuleShape{
|
||||
type_store: src.type_store
|
||||
}
|
||||
println(dst.type_store.types[0].len)
|
||||
println(dst.type_store.types[63].len)
|
||||
println(dst.type_store.types[128].len)
|
||||
println(dst.type_store.types[399].len)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
module main
|
||||
|
||||
fn read_u32_le_like(data []u8, off int) u32 {
|
||||
return u32(data[off]) | (u32(data[off + 1]) << 8) | (u32(data[off + 2]) << 16) | (u32(data[
|
||||
off + 3]) << 24)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
instr := u32(0x910000a5)
|
||||
result := (instr & u32(0xffc003ff)) | (u32(0xa0) << 10)
|
||||
assert result == u32(0x910280a5)
|
||||
|
||||
sym_addr := u64(0x1000240e0)
|
||||
page_off := sym_addr & 0xfff
|
||||
result_from_sym := (instr & u32(0xffc003ff)) | (u32(page_off) << 10)
|
||||
assert result_from_sym == u32(0x910380a5)
|
||||
|
||||
bytes := [u8(0xa5), 0, 0, 0x91]
|
||||
instr_from_bytes := read_u32_le_like(bytes, 0)
|
||||
assert instr_from_bytes == u32(0x910000a5)
|
||||
result_from_bytes := (instr_from_bytes & u32(0xffc003ff)) | (u32(page_off) << 10)
|
||||
assert result_from_bytes == u32(0x910380a5)
|
||||
}
|
||||
Reference in new issue
Block a user