{"id":96895,"date":"2017-08-28T07:00:00","date_gmt":"2017-08-28T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96895"},"modified":"2019-03-13T01:16:04","modified_gmt":"2019-03-13T08:16:04","slug":"20170828-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170828-00\/?p=96895","title":{"rendered":"The Alpha AXP, part 15: Variadic functions"},"content":{"rendered":"<p>As noted in <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20170807-00\/?p=96766\">the initial plunge<\/a>, the first six integer parameters are passed in registers, and the first six floating point parameters are passed in a different set of registers. So <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20170807-00\/?p=96766#comment-1305835\">how does the callee known at function entry which registers to spill, and in what order<\/a>?&sup1; <\/p>\n<p>Answer: It doesn&#8217;t. So it just spills everything. <\/p>\n<p>First, a detail on the calling convention: The first six parameters are passed in registers, and if you pass a parameter in an integer register, then the corresponding floating point register is unused, and vice versa. In other words: <\/p>\n<ul>\n<li>    The first parameter is passed in either <var>a0<\/var> or <var>f16<\/var>. <\/li>\n<li>    The second parameter is passed in either <var>a1<\/var> or <var>f17<\/var>. <\/li>\n<li>&hellip;<\/li>\n<li>    The sixth parameter is passed in either <var>a5<\/var> or <var>f21<\/var>. <\/li>\n<\/ul>\n<p>On entry to a variadic function, the function spills all the integer parameter registers onto the stack first, and then spills the floating point parameter registers onto the stack next. The result is a stack that looks like this: <\/p>\n<table BORDER=\"0\" STYLE=\"border-collapse: collapse\" CELLPADDING=\"3\" CLASS=\"cp3\">\n<tr>\n<td ALIGN=\"center\">&#x22ee;<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">param 10<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">param 9<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">param 8<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">param 7<\/td>\n<td>&larr; stack pointer on function entry<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">integer param 6 (<var>a5<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">integer param 5 (<var>a4<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">integer param 4 (<var>a3<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">integer param 3 (<var>a2<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">integer param 2 (<var>a1<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">integer param 1 (<var>a0<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">floating point param 6 (<var>f21<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">floating point param 5 (<var>f20<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">floating point param 4 (<var>f19<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">floating point param 3 (<var>f18<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">floating point param 2 (<var>f17<\/var>)<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">floating point param 1 (<var>f16<\/var>)<\/td>\n<td>&larr; stack pointer after spilling<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">local variable<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">local variable<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">local variable<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"center\" STYLE=\"border: solid 1px black\">local variable<\/td>\n<td>&larr; stack pointer after prologue complete<\/td>\n<\/tr>\n<\/table>\n<p>The <code>va_list<\/code> type is a structure: <\/p>\n<pre>\ntypedef struct __va_list\n{\n  char* base;\n  size_t offset;\n} va_list;\n<\/pre>\n<p>The <code>va_start<\/code> macro initializes <code>base<\/code> to point to &#8220;integer param 1&#8221; and <code>offset<\/code> to 8 &times; the number of non-variadic parameters. <\/p>\n<p>If you invoke the <code>va_arg<\/code> macro with a non-floating point type as the second parameter, then it operates in an unsurprising manner: It retrieves the data from <code>base + offset<\/code> and then increases the <code>offset<\/code> by the size of the data (rounded up to the nearest multiple of eight). <\/p>\n<p>But invoking the <code>va_arg<\/code> macro with a floating point type as the second parameter is weirder: If the <code>offset<\/code> is less than 48, then it retrieves the data from <code>base + offset - 48<\/code>, resulting in a &#8220;reach-back&#8221; into the parallel array of spilled floating point registers. If the <code>offset<\/code> is greater than or equal to 48, then it retrieves the data from <code>base + offset<\/code> as usual. Regardless of where the data is read from, the <code>offset<\/code> increases by the size of the data (rounded up to the nearest multiple of eight). <\/p>\n<p>The implementations of the <code>va_start<\/code> and <code>va_arg<\/code> macros take advantage of special-purpose compiler intrinsics that did a lot of the magic. <\/p>\n<p>There are a few optimizations possible here. For one thing, the compiler doesn&#8217;t need to spill non-variadic parameters, though it does need to reserve space for them on the stack so that the <code>va_arg<\/code> macro continues to work.&sup2; Furthermore, if the compiler can observe that <code>va_arg<\/code> is never invoked with a floating point type, then it doesn&#8217;t need to spill the floating point registers at all. (Similarly, if <code>va_arg<\/code> is always invoked with floating point types, then the integer registers don&#8217;t need to be spilled.) <\/p>\n<p>I don&#8217;t remember whether the Microsoft compiler actually implemented any of these optimizations. <\/p>\n<p>&sup1; It turns out that this question is not Alpha-specific. It applies to any architecture that passes variadic parameters differently depending on their type. <\/p>\n<p>&sup2; If the compiler can observe that <code>va_arg<\/code> is never invoked with a floating point type, then it doesn&#8217;t even need to reserve space for the non-variadic parameters. It can just point the <code>base<\/code> at where the first integer parameter would have been, even though it now points into the local variables. Those local variables will never be read as parameters because the initial <code>offset<\/code> skips over them. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Keeping two sets of books that eventually become one.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-96895","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Keeping two sets of books that eventually become one.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96895","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=96895"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96895\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=96895"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96895"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96895"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}