ref: 92b12fc6772aae7ebb9b6436dcfc393e17de01b6
dir: /lib/std/varargs.myr/
use "types" use "introspect" use "sleq" use "die" 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 : intptr) + sizeof(byte#) a = (ip : byte#) -> [.args = a, .tc = tc] } extern const put : (fmt : byte[:], args : ... -> size) const vaenter = {ap var ty ty = vatype(ap) match typedesc(ty) | `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=cursoralign(ap.args, ty), .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 cursoralign = {arg, ty var ti, align, p ti = typeinfo(ty) /* apply the alignment to the arg pointer */ align = (ti.align : intptr) p = (arg : intptr) p = (p + align - 1) & ~(align - 1) -> (p : byte#) } const vaenterunion = {ap, elt var sub, ti, p, align ti = typeinfo(tcpeek(&ap.tc)) align = (ti.align : intptr) p = (ap.args : intptr) p = (p + align - 1) & ~(align - 1) ap.args = (p : byte#) 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 : intptr) p = (ap.args : intptr) p = (p + align - 1) & ~(align - 1) ap.args = (p : byte#) sl = ap.args[:ti.size] tcnext(&ap.tc) sz = (ti.size : intptr) ap.args = ((p : intptr) + sz : 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 : intptr) p = (ap.args : 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 : intptr) + sizeof(@a) : byte#) -> (p : @a#)# } const addp = {p, k -> ((p : intptr) + k : byte#) } const sliceptr = {p -> (p : byte##)# } const slicelen = {p p = addp(p, sizeof(intptr)) -> (p : size#)# }