nx_emu_loongarch64.nx
buildroot/runtime/nx_emu_loongarch64.nx
about
nx_emu_loongarch64.nx -- sovereign LoongArch64 (LA64) interpreter (NX-EMU).
Flat 32 GPRs (r0==zero), little-endian, NO register windows, NO delay
slots. Decode/execute pure NishiLang per the LoongArch Reference
Manual Volume 1 (v1.10) -- NO qemu (qemu-loongarch64 stays a
differential BENCHMARK that must agree, never a dependency). How
Nishi CARRIES loongarch64 execution (operator: nishi carries;
externals = bench).
---------------------------------------------------------------------
2026-09-03, ISA LANE. WHAT THIS GENERATION ADDS AND WHY.
The previous generation decoded ELEVEN 3R ops, THREE 2RI12 ops, jirl,
bl and syscall -- and NOT ONE CONDITIONAL BRANCH. An interpreter with
no conditional branch cannot run a loop, cannot run an if, and cannot
run any program a compiler would emit; it could only ever execute
straight-line arithmetic. That is the same gap the mips64 sibling
carried until 2026-09-03, named by nx_isa_conform_gate and closed the
same day, and it is the highest-value thing missing here.
ADDED (each encoding taken from the manual instruction tables, not
from any emulator behaviour):
branches beq bne blt bge bltu bgeu (2RI16) | beqz bnez (1RI21) | b (I26)
compare slt sltu slti sltui
logic nor andn orn andi ori xori maskeqz masknez
shift-imm slli.w slli.d srli.w srli.d srai.w srai.d
32-bit add.w sub.w mul.w sll.w srl.w sra.w addi.w
const lu12i.w lu32i.d pcaddi pcaddu12i pcalau12i
memory ld.b ld.h ld.w st.b st.h st.w ld.bu ld.hu ld.wu
FIXED, a real defect the ruler caught: SRL.D was implemented with
the NishiLang >> operator which is ARITHMETIC, so srl.d and sra.d
were the SAME instruction. It is invisible on every non-negative
value -- the exact shape of a test that cannot fail -- and shows up
only on a negative operand shifted far enough that the sign copies
reach the low byte. Logical right shift now goes through la_srl,
mirroring the rv64 sibling emu_srl, which carries the same comment
for the same reason.
ALSO FIXED: (1) memory accesses are BOUNDS-CHECKED against mem_size
instead of trusting the guest -- an out-of-range guest address used
dependencies 1 imports · 2 importers
imports: nx_syscalls_x86_64.nx
imported by: nx_emu_loongarch64_main.nxnx_isa_loongarch64_gate.nx
structs
| none |
consts
| 67 | const LA_MAGIC_200000000: i64 = 200000000 |
| 69 | const LA_GUEST_SIZE: i64 = 16777216 |
| 70 | const LA_SYS_READ: i64 = 63 |
| 71 | const LA_SYS_WRITE: i64 = 64 |
| 72 | const LA_SYS_EXIT: i64 = 93 |
| 73 | const LA_SYS_EXITG: i64 = 94 |
| 77 | const LAE_UNSUPPORTED: i64 = -1 |
| 78 | const LAE_RANOFF: i64 = -2 |
| 79 | const LAE_FAULT: i64 = -3 |
| 81 | const LA_ALL_ONES: i64 = -1 |
| 82 | const LA_NEG_ONE: i64 = -1 |
| 85 | const LA_POW12: i64 = 4096 |
| 86 | const LA_POW16: i64 = 65536 |
| 87 | const LA_POW20: i64 = 1048576 |
| 88 | const LA_POW21: i64 = 2097152 |
| 89 | const LA_POW26: i64 = 67108864 |
| 90 | const LA_POW32: i64 = 4294967296 |
| 93 | const LA_OP_ADD_W: i64 = 0x00100000 |
| 94 | const LA_OP_ADD_D: i64 = 0x00108000 |
| 95 | const LA_OP_SUB_W: i64 = 0x00110000 |
| 96 | const LA_OP_SUB_D: i64 = 0x00118000 |
| 97 | const LA_OP_SLT: i64 = 0x00120000 |
| 98 | const LA_OP_SLTU: i64 = 0x00128000 |
| 99 | const LA_OP_MASKEQZ: i64 = 0x00130000 |
| 100 | const LA_OP_MASKNEZ: i64 = 0x00138000 |
| 101 | const LA_OP_NOR: i64 = 0x00140000 |
| 102 | const LA_OP_AND: i64 = 0x00148000 |
| 103 | const LA_OP_OR: i64 = 0x00150000 |
| 104 | const LA_OP_XOR: i64 = 0x00158000 |
| 105 | const LA_OP_ORN: i64 = 0x00160000 |
| 106 | const LA_OP_ANDN: i64 = 0x00168000 |
| 107 | const LA_OP_SLL_W: i64 = 0x00170000 |
| 108 | const LA_OP_SRL_W: i64 = 0x00178000 |
| 109 | const LA_OP_SRA_W: i64 = 0x00180000 |
| 110 | const LA_OP_SLL_D: i64 = 0x00188000 |
| 111 | const LA_OP_SRL_D: i64 = 0x00190000 |
| 112 | const LA_OP_SRA_D: i64 = 0x00198000 |
| 113 | const LA_OP_MUL_W: i64 = 0x001c0000 |
| 114 | const LA_OP_MUL_D: i64 = 0x001d8000 |
| 115 | const LA_OP_DIV_D: i64 = 0x00220000 |
| 116 | const LA_OP_MOD_D: i64 = 0x00228000 |
| 117 | const LA_INSN_SYSCALL: i64 = 0x002B0000 |
| 124 | const LA_OP_SLLI_W: i64 = 0x00408000 |
| 125 | const LA_OP_SRLI_W: i64 = 0x00448000 |
| 126 | const LA_OP_SRAI_W: i64 = 0x00488000 |
| 127 | const LA_OP_SLLI_D: i64 = 0x00410000 |
| 128 | const LA_OP_SRLI_D: i64 = 0x00450000 |
| 129 | const LA_OP_SRAI_D: i64 = 0x00490000 |
| 135 | const LA_OP_SLTI: i64 = 0x02000000 |
| 136 | const LA_OP_SLTUI: i64 = 0x02400000 |
| 137 | const LA_OP_ADDI_W: i64 = 0x02800000 |
| 138 | const LA_OP_ADDI_D: i64 = 0x02c00000 |
| 139 | const LA_OP_ANDI: i64 = 0x03400000 |
| 140 | const LA_OP_ORI: i64 = 0x03800000 |
| 141 | const LA_OP_XORI: i64 = 0x03c00000 |
| 142 | const LA_OP_LD_B: i64 = 0x28000000 |
| 143 | const LA_OP_LD_H: i64 = 0x28400000 |
| 144 | const LA_OP_LD_W: i64 = 0x28800000 |
| 145 | const LA_OP_LD_D: i64 = 0x28c00000 |
| 146 | const LA_OP_ST_B: i64 = 0x29000000 |
| 147 | const LA_OP_ST_H: i64 = 0x29400000 |
| 148 | const LA_OP_ST_W: i64 = 0x29800000 |
| 149 | const LA_OP_ST_D: i64 = 0x29c00000 |
| 150 | const LA_OP_LD_BU: i64 = 0x2a000000 |
| 151 | const LA_OP_LD_HU: i64 = 0x2a400000 |
| 152 | const LA_OP_LD_WU: i64 = 0x2a800000 |
| 155 | const LA_OP_LU12I_W: i64 = 0x14000000 |
| 156 | const LA_OP_LU32I_D: i64 = 0x16000000 |
| 157 | const LA_OP_PCADDI: i64 = 0x18000000 |
| 158 | const LA_OP_PCALAU12I: i64 = 0x1a000000 |
| 159 | const LA_OP_PCADDU12I: i64 = 0x1c000000 |
| 162 | const LA_OP_BEQZ: i64 = 0x40000000 |
| 163 | const LA_OP_BNEZ: i64 = 0x44000000 |
| 164 | const LA_OP_JIRL: i64 = 0x4c000000 |
| 165 | const LA_OP_B: i64 = 0x50000000 |
| 166 | const LA_OP_BL: i64 = 0x54000000 |
| 167 | const LA_OP_BEQ: i64 = 0x58000000 |
| 168 | const LA_OP_BNE: i64 = 0x5c000000 |
| 169 | const LA_OP_BLT: i64 = 0x60000000 |
| 170 | const LA_OP_BGE: i64 = 0x64000000 |
| 171 | const LA_OP_BLTU: i64 = 0x68000000 |
| 172 | const LA_OP_BGEU: i64 = 0x6c000000 |
| 175 | const LA_MASK_3R: i64 = 0xFFFF8000 |
| 176 | const LA_MASK_3RI6: i64 = 0xFFFF0000 |
| 177 | const LA_MASK_2RI12: i64 = 0xFFC00000 |
| 178 | const LA_MASK_1RI20: i64 = 0xFE000000 |
| 179 | const LA_MASK_OP6: i64 = 0xFC000000 |
| 181 | const LA_REG_COUNT: i64 = 32 |
| 182 | const LA_REG_BYTES: i64 = 256 // 32 registers of 8 bytes |
| 183 | const LA_REG_SP: i64 = 3 // sp is r3 |
| 184 | const LA_REG_RA: i64 = 1 // ra is r1 |
| 185 | const LA_REG_A0: i64 = 4 // a0 is r4 |
| 186 | const LA_REG_A1: i64 = 5 |
| 187 | const LA_REG_A2: i64 = 6 |
| 188 | const LA_REG_A7: i64 = 11 // a7 is r11, it carries the syscall number |
functions
| 192 | func la_g_ld(mem: *u8, va: i64, width: i64) -> i64 { // little-endian, zero-fill |
| 202 | func la_g_ld_s(mem: *u8, va: i64, width: i64) -> i64 { // little-endian, sign-extended |
| 211 | func la_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 called by 1: emu_loongarch64_run_mem |
| 222 | func la_ea_ok(ea: i64, width: i64, mem_size: i64) -> i64 called by 1: emu_loongarch64_run_mem |
| 228 | func la_rd(r: *i64, n: i64) -> i64 { if n == 0 { return 0 } return r[n] } called by 1: emu_loongarch64_run_mem |
| 229 | func la_wr(r: *i64, n: i64, v: i64) -> i64 { if n != 0 { r[n] = v } return 0 } called by 1: emu_loongarch64_run_mem |
| 233 | func la_srl(v: i64, n: i64) -> i64 called by 1: emu_loongarch64_run_mem |
| 240 | func la_ltu(a: i64, b: i64) -> i64 called by 1: emu_loongarch64_run_mem |
| 246 | func la_sext32(v: i64) -> i64 called by 1: emu_loongarch64_run_mem |
| 251 | func la_sext12(v: i64) -> i64 { if (v & 0x800) != 0 { return v - LA_POW12 } return v } called by 1: emu_loongarch64_run_mem |
| 252 | func la_sext16(v: i64) -> i64 { if (v & 0x8000) != 0 { return v - LA_POW16 } return v } called by 1: emu_loongarch64_run_mem |
| 253 | func la_sext20(v: i64) -> i64 { if (v & 0x80000) != 0 { return v - LA_POW20 } return v } called by 1: emu_loongarch64_run_mem |
| 254 | func la_sext21(v: i64) -> i64 { if (v & 0x100000) != 0 { return v - LA_POW21 } return v } called by 1: emu_loongarch64_run_mem |
| 255 | func la_sext26(v: i64) -> i64 { if (v & 0x2000000) != 0 { return v - LA_POW26 } return v } called by 1: emu_loongarch64_run_mem |
| 259 | func emu_loongarch64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 |
| 509 | func emu_loongarch64_load_elf(buf: *u8, len: i64) -> i64 |