Skip to content

builtin.closure #

The files in this directory implement the closure feature of the V language, which is called internally by the V compiler.

Constants #

const closure_thunk = $if ppc64le {
	[
		u8(0xa6),
		0x02,
		0x08,
		0x7c, // mflr   %r0
		0x05,
		0x00,
		0x00,
		0x48, // bl     here
		0xa6,
		0x02,
		0xc8,
		0x7d, // here:  mflr %r14
		0xf8,
		0xbf,
		0xce,
		0x39, // addi   %r14, %r14, -16392
		0x00,
		0x00,
		0xce,
		0xc9, // lfd    %f14, 0(%r14)
		0x08,
		0x00,
		0xce,
		0xe9, // ld     %r14, 8(%r14)
		0x78,
		0x73,
		0xcc,
		0x7d, // mr     %r12, %r14
		0xa6,
		0x03,
		0x08,
		0x7c, // mtlr   %r0
		0xa6,
		0x03,
		0xc9,
		0x7d, // mtctr  %r14
		0x20,
		0x04,
		0x80,
		0x4e, // bctr
	]!
} $else $if !ppc64le && !amd64 && !i386 && !arm64 && !arm32 && !rv64 && !rv32 && !s390x
	&& !loongarch64 {
	// ppc (32-bit PowerPC) - expressed as negation of all other arches for bootstrap compat
	[
		u8(0x7c),
		0x08,
		0x02,
		0xa6, // mflr   %r0
		0x48,
		0x00,
		0x00,
		0x05, // bl     here
		0x7d,
		0x88,
		0x02,
		0xa6, // here:  mflr %r12
		0x39,
		0x8c,
		0xbf,
		0xf8, // addi   %r12, %r12, -16392
		0xc9,
		0xcc,
		0x00,
		0x00, // lfd    %f14, 0(%r12)
		0x81,
		0x8c,
		0x00,
		0x04, // lwz    %r12, 4(%r12)
		0x7c,
		0x08,
		0x03,
		0xa6, // mtlr   %r0
		0x7d,
		0x89,
		0x03,
		0xa6, // mtctr  %r12
		0x4e,
		0x80,
		0x04,
		0x20, // bctr
	]!
} $else $if amd64 {
	[
		u8(0xF3),
		0x44,
		0x0F,
		0x7E,
		0x3D,
		0xF7,
		0xBF,
		0xFF,
		0xFF, // movq  xmm15, QWORD PTR [rip - userdata]
		0xFF,
		0x25,
		0xF9,
		0xBF,
		0xFF,
		0xFF, // jmp  QWORD PTR [rip - fn]
	]!
} $else $if i386 {
	[
		u8(0xe8),
		0x00,
		0x00,
		0x00,
		0x00, // call here
		// here:
		0x59, // pop  ecx
		0x66,
		0x0F,
		0x6E,
		0xF9, // movd xmm7, ecx
		0xff,
		0xA1,
		0xff,
		0xbf,
		0xff,
		0xff, // jmp  DWORD PTR [ecx - 0x4001] # <fn>
	]!
} $else $if arm64 {
	[
		u8(0x11),
		0x00,
		0xFE,
		0x5C, // ldr d17, userdata
		0x30,
		0x00,
		0xFE,
		0x58, // ldr x16, fn
		0x00,
		0x02,
		0x1F,
		0xD6, // br  x16
	]!
} $else $if arm32 {
	[
		u8(0x04),
		0xC0,
		0x4F,
		0xE2, // adr ip, here
		// here:
		0x01,
		0xC9,
		0x4C,
		0xE2, // sub  ip, ip, #0x4000
		0x90,
		0xCA,
		0x07,
		0xEE, // vmov s15, ip
		0x00,
		0xC0,
		0x9C,
		0xE5, // ldr  ip, [ip, 0]
		0x1C,
		0xFF,
		0x2F,
		0xE1, // bx   ip
	]!
} $else $if rv64 {
	[
		u8(0x97),
		0xCF,
		0xFF,
		0xFF, // auipc t6, 0xffffc
		0x03,
		0xBF,
		0x8F,
		0x00, // ld    t5, 8(t6)
		0x07,
		0xB3,
		0x0F,
		0x00, // fld   ft6, 0(t6)
		0x67,
		0x00,
		0x0F,
		0x00, // jr    t5
	]!
} $else $if rv32 {
	[
		u8(0x97),
		0xCF,
		0xFF,
		0xFF, // auipc t6, 0xffffc
		0x03,
		0xAF,
		0x4F,
		0x00, // lw    t5, 4(t6)
		0x07,
		0xAB,
		0x0F,
		0x00, // flw   fs6, 0(t6)
		0x67,
		0x00,
		0x0F,
		0x00, // jr    t5
	]!
} $else $if s390x {
	[
		u8(0xC0),
		0x10,
		0xFF,
		0xFF,
		0xE0,
		0x00, // larl %r1, -16384
		0x68,
		0xF0,
		0x10,
		0x00, // ld   %f15, 0(%r1)
		0xE3,
		0x10,
		0x10,
		0x08,
		0x00,
		0x04, // lg   %r1, 8(%r1)
		0x07,
		0xF1, // br   %r1
	]!
} $else $if loongarch64 {
	[
		u8(0x92),
		0xFF,
		0xFF,
		0x1D, // pcaddu12i t6, -4
		0x48,
		0x02,
		0x80,
		0x2B, // fld.d     f8, t6, 0
		0x51,
		0x22,
		0xC0,
		0x28, // ld.d      t5, t6, 8
		0x20,
		0x02,
		0x00,
		0x4C, // jr        t5
	]!
} $else $if sparc64 {
	[
		u8(0x83),
		0x41,
		0x40,
		0x00, // rd  %pc, %g1
		0x05,
		0x00,
		0x00,
		0x10, // sethi  %hi(0x4000), %g2
		0x84,
		0x10,
		0xa0,
		0x00, // mov  %g2, %g2   ! 4000 <main>
		0x82,
		0x20,
		0x40,
		0x02, // sub  %g1, %g2, %g1
		0xff,
		0x18,
		0x60,
		0x00, // ldd  [ %l1 ], %d62
		0xc2,
		0x58,
		0x60,
		0x08, // ldx  [ %g1 + 8 ], %g1
		0x81,
		0xc0,
		0x40,
		0x00, // jmp  %g1
		0x01,
		0x00,
		0x00,
		0x00, // nop
	]!
} $else $if big_endian {
	[
		u8(0x7C),
		0x08,
		0x02,
		0xA6, // mflr   %r0
		0x48,
		0x00,
		0x00,
		0x05, // bl     here
		0x7D,
		0xC8,
		0x02,
		0xA6, // here:  mflr %r14
		0x39,
		0xCE,
		0xC0,
		0x08, // addi   %r14, %r14, -16376
		0xC9,
		0xCE,
		0x00,
		0x00, // lfd    %f14, 0(%r14)      // userdata
		0xE9,
		0xCE,
		0x00,
		0x08, // ld     %r14, 8(%r14)      // func descriptor ptr
		0xE9,
		0x8E,
		0x00,
		0x00, // ld     %r12, 0(%r14)      // code addr from descriptor
		0xE8,
		0x4E,
		0x00,
		0x08, // ld     %r2,  8(%r14)      // TOC from descriptor
		0x7C,
		0x08,
		0x03,
		0xA6, // mtlr   %r0
		0x7D,
		0x89,
		0x03,
		0xA6, // mtctr  %r12
		0x4E,
		0x80,
		0x04,
		0x20, // bctr
	]!
} $else {
	[u8(0)]!
}

refer to https://godbolt.org/z/r7P3EYv6c for a complete assembly

Note: Keep the first branch as the longest byte sequence. In translated/bootstrap C mode(vc/v.c), V emits a fixed C array whose size is inferred from the first branch. The final big_endian branch maps to ppc64 here, since the supported big-endian closure targets handled above are s390x and sparc64. vfmt off

fn lifetime_state_allocs #

fn lifetime_state_allocs(out &u64)

lifetime_state_allocs writes the number of allocated closure lifetime bookkeeping states to out.

fn new_lifetime #

fn new_lifetime() Lifetime

new_lifetime creates a lifetime object for tracking closure callbacks created inside frames.

fn MemoryProtectAtrr.from #

fn MemoryProtectAtrr.from[W](input W) !MemoryProtectAtrr

struct Lifetime #

struct Lifetime {
mut:
	state      &ClosureLifetimeState = unsafe { nil }
	generation u64
	disposed   bool
}

Lifetime owns a set of tracked closure callbacks that can be reclaimed together.

fn (Lifetime) frame #

fn (mut lifetime Lifetime) frame(work fn ()) !

frame runs work while tracking closure callbacks created by that work in this lifetime. The work callback itself is borrowed; only closures allocated while the frame is active are owned by the lifetime and later released by reclaim or dispose.

fn (Lifetime) reclaim #

fn (mut lifetime Lifetime) reclaim(retain int) !

reclaim releases tracked closure callbacks from old frames while retaining the newest retain frames.

fn (Lifetime) reclaim_all #

fn (mut lifetime Lifetime) reclaim_all() !

reclaim_all releases all tracked closure callbacks owned by this lifetime.

fn (Lifetime) dispose #

fn (mut lifetime Lifetime) dispose() !

dispose releases all tracked closure callbacks and invalidates this lifetime.

fn (Lifetime) suspend #

fn (mut lifetime Lifetime) suspend(work fn ()) !

suspend runs work without tracking closure callbacks in the active frame of this lifetime. The work callback is borrowed and is not owned or released by the lifetime.

fn (Lifetime) untracked #

fn (mut lifetime Lifetime) untracked(work fn ()) !

untracked runs work without tracking closure callbacks in this lifetime. The work callback is borrowed and is not owned or released by the lifetime.