// Handwritten ABI probe (design §10.3): freezes the .s ↔ runtime contract
// independently of the lowering pass. Covers rt_globals_init, strings,
// builtin call via rt_call, closures with 0 / 2 / 7 parameters (the full
// x1..x7 argument range), SMI ↔ boxed integer equality, and the
// rt_construct/rt_call distinction.
//
// Expected stdout: "abi\n3\n28\n7\n", exit code 0 (42 on assertion failure).
.text
.globl main
.p2align 2
main:
stp x29, x30, [sp, #-16]!
mov x29, sp
adrp x0, g_globals
add x0, x0, :lo12:g_globals
movz x1, #0
bl rt_globals_init
// puts("abi") through rt_call: builtin puts = (1 << 3) | 0b101 = 0xd.
adrp x0, .Lstr_abi
add x0, x0, :lo12:.Lstr_abi
movz x1, #3
bl rt_string_from_bytes
sub sp, sp, #16
str x0, [sp, #8] // arg 0
movz x0, #0xd
str x0, [sp] // callee
ldr x0, [sp]
movz x1, #1
add x2, sp, #8
bl rt_call
add sp, sp, #16
// add2 = closure(.Lfn_add2, 2 params); puts(add2(1, 2)) -> "3".
adrp x0, .Lfn_add2
add x0, x0, :lo12:.Lfn_add2
movz x1, #2
mov x2, sp // no captures: base unused at len 0
movz x3, #0
bl rt_closure
sub sp, sp, #32
str x0, [sp] // callee
movz x0, #2 // SMI 1
str x0, [sp, #8]
movz x0, #4 // SMI 2
str x0, [sp, #16]
ldr x0, [sp]
movz x1, #2
add x2, sp, #8
bl rt_call
add sp, sp, #32
bl print_acc
// sum7 = closure(.Lfn_sum7, 7 params); puts(sum7(1..7)) -> "28".
adrp x0, .Lfn_sum7
add x0, x0, :lo12:.Lfn_sum7
movz x1, #7
mov x2, sp
movz x3, #0
bl rt_closure
sub sp, sp, #64 // callee + 7 args
str x0, [sp]
movz x0, #2
str x0, [sp, #8]
movz x0, #4
str x0, [sp, #16]
movz x0, #6
str x0, [sp, #24]
movz x0, #8
str x0, [sp, #32]
movz x0, #10
str x0, [sp, #40]
movz x0, #12
str x0, [sp, #48]
movz x0, #14
str x0, [sp, #56]
ldr x0, [sp]
movz x1, #7
add x2, sp, #8
bl rt_call
add sp, sp, #64
bl print_acc
// zero-argument closure -> SMI 7; puts prints "7".
adrp x0, .Lfn_zero
add x0, x0, :lo12:.Lfn_zero
movz x1, #0
mov x2, sp
movz x3, #0
bl rt_closure
sub sp, sp, #16
str x0, [sp]
ldr x0, [sp]
movz x1, #0
add x2, sp, #8
bl rt_call
add sp, sp, #16
bl print_acc
// SMI <-> boxed: rt_box_int(5) == SMI 5 must be true (0b0111).
movz x0, #5
bl rt_box_int
movz x1, #10
bl rt_eq
cmp x0, #7
b.ne .Lfail
// rt_construct is selected by node kind, not callee type: a class with
// no constructor and zero arguments yields a (truthy) instance.
adrp x0, .Lstr_cls
add x0, x0, :lo12:.Lstr_cls
movz x1, #1
bl rt_class
sub sp, sp, #16
str x0, [sp]
ldr x0, [sp]
movz x1, #0
add x2, sp, #8
bl rt_construct
add sp, sp, #16
bl rt_truthy
cmp x0, #1
b.ne .Lfail
mov w0, #0
mov sp, x29
ldp x29, x30, [sp], #16
ret
.Lfail:
mov w0, #42
mov sp, x29
ldp x29, x30, [sp], #16
ret
// print_acc: puts(x0), preserving nothing (helper, not part of the ABI).
print_acc:
stp x29, x30, [sp, #-16]!
mov x29, sp
sub sp, sp, #16
str x0, [sp, #8]
movz x0, #0xd
str x0, [sp]
ldr x0, [sp]
movz x1, #1
add x2, sp, #8
bl rt_call
mov sp, x29
ldp x29, x30, [sp], #16
ret
// fn add2(closure x0, a x1, b x2) = a + b.
.Lfn_add2:
stp x29, x30, [sp, #-16]!
mov x29, sp
mov x0, x1
mov x1, x2
bl rt_add
mov sp, x29
ldp x29, x30, [sp], #16
ret
// fn sum7(closure x0, a1..a7 in x1..x7): arguments are caller-saved, so
// spill all seven before the first rt_add.
.Lfn_sum7:
stp x29, x30, [sp, #-16]!
mov x29, sp
sub sp, sp, #64
str x1, [sp]
str x2, [sp, #8]
str x3, [sp, #16]
str x4, [sp, #24]
str x5, [sp, #32]
str x6, [sp, #40]
str x7, [sp, #48]
ldr x0, [sp]
ldr x1, [sp, #8]
bl rt_add
ldr x1, [sp, #16]
bl rt_add
ldr x1, [sp, #24]
bl rt_add
ldr x1, [sp, #32]
bl rt_add
ldr x1, [sp, #40]
bl rt_add
ldr x1, [sp, #48]
bl rt_add
mov sp, x29
ldp x29, x30, [sp], #16
ret
// fn zero(closure x0) = 7. Leaf: x30 stays intact, plain ret.
.Lfn_zero:
movz x0, #14
ret
.section .rodata
.Lstr_abi:
.byte 0x61, 0x62, 0x69
.Lstr_cls:
.byte 0x43
.bss
.balign 8
g_globals:
.skip 0