ref: 515b8e89c1a6c0859eaefbf7835a4e6497a57e0b
dir: /libstd/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[:]) 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] } 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 } const inspectarg = {x } generic vanext = {ap -> @a var v : @a var ti var align var p ti = typeinfo(tcpeek(&ap.tc)) /* apply the alignment to the arg pointer */ align = ti.align castto(intptr) inspectarg(align) p = ap.args castto(intptr) p = (p + align - 1) & ~(align - 1) ap.args = p castto(byte#) v = (ap.args castto(@a#))# /* 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#) -> v }