ref: 7ef2abad32fe3b273f16eeb28d63a63229dca3a6
dir: /lib/std/varargs.myr/
use "types.use"
use "introspect.use"
use "sleq.use"
use "die.use"
pkg std =
type valist
const vastart : (args : ...# -> valist)
const vatype : (ap : valist# -> byte[:])
const vabytes : (ap : valist# -> byte[:])
const vaenter : (ap : valist# -> valist)
const vaenterunion : (ap : valist#, elt : int32 -> valist)
generic vanext : (ap : valist# -> @a)
;;
type valist = struct
args : byte#
tc : typecursor
;;
/*
* a valist is really just a pointer to the varargs.
* we assume that these sit on the stack nicely,
* and don't need special handling to get to.
*
* This will be a problem when we switch to a
* register based convention. We might want to
* force varargs onto the stack regardless.
*/
const vastart = {args
var tc, a, ip
/*
pull out the args. These are on the stacks like so:
[ required ]
[ args ]
---variadic---
[ typeinfo ] --> type description
------------
[ variadic ]
[ args ]
[ here ]
&args points to the typeinfo, &args + sizeof(void#)
points to the rest argument.
*/
tc = typeenc(args)
ip = (args castto(intptr)) + sizeof(byte#)
a = ip castto(byte#)
-> [.args = a, .tc = tc]
}
extern const put : (fmt : byte[:], args : ... -> size)
const vaenter = {ap
match typedesc(vatype(ap))
| `Tyslice enc: -> [.args=sliceptr(ap.args), .tc=[.nelt=slicelen(ap.args), .rem=enc, .isiter=false]]
| `Tytuple tc: -> [.args=ap.args, .tc=tc]
| `Tystruct tc: -> [.args=ap.args, .tc=tc]
| `Tyarray (sz, enc): -> [.args=ap.args, .tc=[.nelt=sz, .rem=enc, .isiter=false]]
| `Tyname (name, enc): -> [.args=ap.args, .tc=typeenccursor(enc)]
| _: std.die("unable to enter type\n")
;;
}
const vaenterunion = {ap, elt
var sub
match typedesc(vatype(ap))
| `Tyunion nc:
for var i = 0; i < elt; i++
ncnext(&nc)
;;
(_, sub) = ncnext(&nc)
-> [.args=addp(ap.args, 4), .tc=typeenccursor(sub)]
| t: std.die("type is not a union\n")
;;
}
const vatype = {ap
-> tcpeek(&ap.tc)
}
const vabytes = {ap
var sl
var ti, align, sz
var p
ti = typeinfo(tcpeek(&ap.tc))
/* apply the alignment to the arg pointer */
align = ti.align castto(intptr)
p = ap.args castto(intptr)
p = (p + align - 1) & ~(align - 1)
ap.args = p castto(byte#)
sl = ap.args[:ti.size]
tcnext(&ap.tc)
sz = ti.size castto(intptr)
ap.args = ((p castto(intptr)) + sz) castto(byte#)
-> sl
}
generic vanext = {ap -> @a
var ti
var align
var p
ti = typeinfo(tcpeek(&ap.tc))
/* apply the alignment to the arg pointer */
align = ti.align castto(intptr)
p = ap.args castto(intptr)
p = (p + align - 1) & ~(align - 1)
/* TODO: check for type mismatch */
tcnext(&ap.tc)
/* only move on after we read through the value */
ap.args = ((p castto(intptr)) + sizeof(@a)) castto(byte#)
-> (p castto(@a#))#
}
const addp = {p, k
-> (p castto(intptr)) + k castto(byte#)
}
const sliceptr = {p
-> (p castto(byte##))#
}
const slicelen = {p
p = addp(p, sizeof(intptr))
-> (p castto(size#))#
}