summaryrefslogtreecommitdiff
path: root/lib/std/varargs.myr
blob: f715b7c1ffcf6920824d7cc9646ae66c3b0220f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use "types"
use "introspect"
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#)#
}