LinxISA Assembly Agent Guide¶
This guide is the canonical agent-facing reference for writing LinxISA assembly in libc/runtime bring-up work.
1) Scalar ABI (linx64 Linux userspace)¶
Source of truth:
${LINUX_ROOT}/Documentation/linxisa/abi.md
Register contract:
R0=zero(constant 0)R1=spR2..R9=a0..a7(arguments/return + syscall args)R10=raR11..R19=s0..s8(callee-saved)R20..R23=x0..x3(caller scratch)
Calling convention:
- Integer/pointer return:
a0 - Integer/pointer args:
a0..a7, then stack - Callee-saved:
s0..s8andsp - Caller-saved:
a*,ra,x0..x3 - Stack alignment: 16 bytes
Thread pointer:
- TLS base is modeled in SSR
0x0000(TP) and accessed throughssrget/ssrset.
2) Block-Structured Control Flow Rules¶
Linx is block-structured; control flow must target legal block boundaries.
Required patterns:
- Use
BSTART .../BSTOPorC.BSTART .../C.BSTOParound control-flow regions. - Use direct block branches for static labels:
C.BSTART DIRECT, <label>. - Use conditional block branches with
SETCpredicate in the same block: C.BSTART COND, <label>setc.<cond> ...C.BSTOP- Use indirect block transfer via target register:
C.BSTART INDsetc.tgt <reg>C.BSTOP
Practical rule:
- Do not emit ad-hoc fallthrough/jump mixes that bypass block enter/exit markers.
3) Precise Call/Ret Contract (Mandatory)¶
Normal function form:
- Use
FENTRYat function entry. - Use
FRET.STKfor normal returns. - Canonical pair is
FENTRY + FRET.STK.
Tail-call form:
- Tail-call functions still enter with
FENTRY. - Use
FEXITfor tail-transfer exit. - Canonical tail pair is
FENTRY + FEXIT, then block-legal transfer to callee.
RET/IND/ICALL target setup:
BSTART.RETrequiressetc.tgtto define target source.- Canonical return block is:
C.BSTART.RETc.setc.tgt raC.BSTOPINDandICALLblocks also require explicitsetc.tgtin the same block.
Fused call header (returning calls):
CALLis emitted as fused header pair:BSTART.CALL- immediate adjacent
C.SETRET(orSETRETin non-compressed form) - No instruction may appear between
BSTART.CALLandSETRET/C.SETRET. SETRETdefines an explicit return block label (ratarget); do not assume return is lexical fall-through.
Non-fallthrough return example:
BSTART.STD CALL, callee
setret .Lresume, ->ra
... call block body ...
C.BSTOP
... other blocks ...
.Lresume:
C.BSTART.STD
Setret width guidance:
- Prefer smallest legal form (
c.setret, thensetret). - Use
hl.setretwhen target range/layout cannot be encoded by smaller forms. hl.setretis part of normal correctness/relaxation support, not a special-case extension.
Non-returning call form:
BSTART.CALLwithoutSETRETis valid only for non-returning transfer paths.- In this form,
rais preserved; any eventual return must still satisfy dynamic target safety.
4) Linux Syscall Template¶
Linx Linux userspace syscall ABI:
- Syscall number in
a7 - Args in
a0..a5 - Trap with
acrc 1 - Return in
a0(<0means-errno)
Reference template:
C.BSTART.STD
c.movr <arg0>, ->a0
c.movr <arg1>, ->a1
c.movr <arg2>, ->a2
c.movr <arg3>, ->a3
c.movr <arg4>, ->a4
c.movr <arg5>, ->a5
c.movr <nr>, ->a7
acrc 1
C.BSTART.STD RET
Notes:
- Keep
"memory"clobber for inline-asm syscall helpers. - Use
__syscall_retwhen libc API needs errno canonicalization.
5) setjmp / sigsetjmp / longjmp Invariants¶
Linx64 jmp ABI save set:
s0..s8,sp,ra(11 slots total)
Rules:
setjmpstores exactly the call-preserved set above.longjmprestores exactly that set and returns with:ret = valifval != 0ret = 1ifval == 0(C standard)sigsetjmp(env, savemask):savemask == 0behaves like plainsetjmpsavemask != 0must route through__sigsetjmp_tailso mask save/restore is symmetric acrosssiglongjmp.
6) Signal Restorer Protocol (rt_sigreturn)¶
Linux contract:
- Userspace restorer symbol (
__restore_rt) must issuert_sigreturnsyscall (a7=139,acrc 1). SA_RESTORER(0x04000000) must be available in userspace signal ABI.
Kernel side reference:
${LINUX_ROOT}/arch/linx/kernel/signal.c
Userspace requirements:
__restore/__restore_rtmust be arch implementations (no-op fallback is invalid for full Linux signal ABI).
7) Unwind and CFI Policy¶
Policy for bring-up parity:
- Preserve ABI-stable frame behavior in hand-written asm:
- keep
sp16-byte aligned at call boundaries - preserve/restore callee-saved set exactly
- For stubs that intentionally do not unwind through normal return paths (
__restore_rt,__unmapself,exitpaths), treat them as noreturn terminal stubs. - Avoid synthetic register save layouts that diverge from ABI docs;
setjmp/context save structs must match exported headers.
8) Linux Context-Switch / Trap Patterns to Mirror¶
Primary references:
${LINUX_ROOT}/arch/linx/kernel/switch_to.S${LINUX_ROOT}/arch/linx/kernel/entry.S${LINUX_ROOT}/arch/linx/kernel/signal.c
Patterns to reuse in userspace arch asm:
- Save/restore order for callee-saved state (
s0..s8,sp,ra) is stable and explicit. - Trap/return flow uses SSR snapshots and restores the exact interrupted context.
- Signal frame setup/restore expects userspace restorer + aligned frame.
Agent checklist before submitting asm:
- Are block markers legal and balanced?
- Are returning
CALLheaders fused (BSTART.CALL+ adjacentSETRET/C.SETRET), and non-returning calls explicitly intentional? - Do all
RET/IND/ICALLblocks set target viasetc.tgt? - Is ABI save/restore set minimal and correct?
- Does syscall path use
a7 + acrc 1? - Are signal restorer and
SA_RESTORERwired? - Does
longjmpnormalize0 -> 1? - Are context structs consistent with Linux UAPI headers?