EmbeddedRelated.com
Forums
Memfault Beyond the Launch

C compiler parameter passing question for the MSP430 target

Started by Jonathan Kirwan December 29, 2003
I seem to recall the C compiler vendor representatives all
appearing to claim that they place up to a certain number of
parameters in registers, in the MSP430 case.  If memory serves,
that certain number was the first 4 or so?  Anyway, a question
arises regarding separate compilation and this compiler
behavior.

Given these prototypes for functions:

/* abc() does not take the address of any of its parameters */
  void abc (int a, int b, int c);

/* def() takes the address of parameter b */
  void def (int a, int b, int c);

/* uvw() uses va_start() on parameter a */
  void uvw (int a, int b, int c, ...);

/* xyz() uses va_start() on parameter c */
  void xyz (int a, int b, int c, ...);

Other translation units, having only access to the declarations
above and not the definitions, would assume to pass parameters
in the registers for parameters a, b, and c.  Yes?

And in the case where there is a variable parameter list, all of
the parameters provided in a function call to uvw() or xyz(),
beyond those for a, b, and c, would be placed on the stack.
Yes?  Or is everything for such functions always placed on the
stack, to start?

And when compiling these functions in the modules where they are
defined, not just declared, the compiler will notice that in
abc()'s case, no address is taken so the parameters can be
allowed to stay in the registers, while in def()'s case, the
compiler will notice that there is an address to be taken for
parameter b so it will allocate temporary local auto storage on
the stack for b and spill it from the register before the
address is taken.  Yes?

Similarly, in the case for uvw() and xyz() when the va_start()
is applied to one of the parameters, at that point all of the
register values for parameters *after* the one specified in
va_start() must be spilled into *consecutively addressed* memory
locations in such a way that va_arg() can correctly find them.
Yes?  (Or they were always on the stack to begin with, I
suppose.)

Just curious about the details, here.

Jon


Beginning Microcontrollers with the MSP430

On Mon, Dec 29, 2003 at 02:48:28PM -0800, Jonathan Kirwan wrote:
> Other translation units, having only access to the
declarations
> above and not the definitions, would assume to pass parameters
> in the registers for parameters a, b, and c.  Yes?

Maybe. Depending on the implementation of va_start(), it's possible
that the last parameter before the variable list (the "placeholder")
will always be passed on the stack. That's quite common, though
not strictly necessary.

> And in the case where there is a variable
parameter list, all of
> the parameters provided in a function call to uvw() or xyz(),
> beyond those for a, b, and c, would be placed on the stack.

Yes.

> And when compiling these functions in the modules
where they are
> defined, not just declared, the compiler will notice that in
> abc()'s case, no address is taken so the parameters can be
> allowed to stay in the registers, while in def()'s case, the
> compiler will notice that there is an address to be taken for
> parameter b so it will allocate temporary local auto storage on
> the stack for b and spill it from the register before the
> address is taken.  Yes?

Yes.

> Similarly, in the case for uvw() and xyz() when
the va_start()
> is applied to one of the parameters, at that point all of the
> register values for parameters *after* the one specified in
> va_start() must be spilled into *consecutively addressed* memory
> locations in such a way that va_arg() can correctly find them.
> Yes?  (Or they were always on the stack to begin with, I
> suppose.)

They were probably already on the stack. Since they can't be addressed
directly, and their number is unlimited, it would make little sense
to load them into registers.

Clyde

-- 
Clyde Stubbs                     |            HI-TECH Software
Email: clyde@clyd...          |          Phone            Fax
WWW:   http://www.htsoft.com/    | USA: (408) 490 2885  (408) 490 2885
PGP:   finger clyde@clyd...   | AUS: +61 7 3552 7777 +61 7 3552 7778
---
HI-TECH C: compiling the real world.

> > /* uvw() uses va_start() on parameter a */
> > void uvw (int a, int b, int c, ...);

> > /* xyz() uses va_start() on parameter c */
> > void xyz (int a, int b, int c, ...);

> > Similarly, in the case for uvw() and xyz()
when the va_start()
> > is applied to one of the parameters, at that point all of the
> > register values for parameters *after* the one specified in
> > va_start() must be spilled into *consecutively addressed* memory
> > locations in such a way that va_arg() can correctly find them.
> > Yes?  (Or they were always on the stack to begin with, I
> > suppose.)
> 
> They were probably already on the stack. Since they can't be 
addressed
> directly, and their number is unlimited, it would
make little sense
> to load them into registers.

Yes, in any implementation I've seen, the caller stores 
the "placeholder" c and all ... parameters on the stack.
I think there is no other way, because it's impossible to know what 
is passed for ... at compile time. The application has to walk 
through the parameter list at runtime, depending on a format string 
for example. As you can pass short, long, double etc, the parameters 
might not even fit into a register.
And it's even possible to take the pointer to the parameter list and 
pass it to another function (like vsprintf). So the "..." parameters 
must be all on the stack, otherwise the called function would have to 
build a list in memory which contains also the "..." parameters that 
were passed in registers.

As for uvw(): I wouldn't use va_start() on parameter a, because the 
compiler might be free to pass a and b in registers. The caller 
doesn't know where the called function starts parameter scanning, and 
if it passes a and b in registers, va_start(a) will certainly fail. 
The only solution to this would be to pass all parameters on the 
stack as soon as there is a "...".
IIRC, ANSI states that va_start() has to be used with the last 
parameter before the "...", but I'm not sure (maybe Rolf F. knows

better ;-)

Wolfgang



On Tue, 30 Dec 2003 10:02:56 +1000, you wrote:

>On Mon, Dec 29, 2003 at 02:48:28PM -0800, Jonathan
Kirwan wrote:
>> Other translation units, having only access to the declarations
>> above and not the definitions, would assume to pass parameters
>> in the registers for parameters a, b, and c.  Yes?
>
>Maybe. Depending on the implementation of va_start(), it's possible
>that the last parameter before the variable list (the
"placeholder")
>will always be passed on the stack. That's quite common, though
>not strictly necessary.

Okay.  But it's also legal to use a parameter prior to the last
one before the placeholder.  An example of this, in fact, is in
the more recent standard.  So it's not just the last one which
may be used with va_start().

>> And in the case where there is a variable
parameter list, all of
>> the parameters provided in a function call to uvw() or xyz(),
>> beyond those for a, b, and c, would be placed on the stack.
>
>Yes.

It seemed there could be no other way.  But I like to leave open
the options, just in case someone can tell me something "new."

>> And when compiling these functions in the
modules where they are
>> defined, not just declared, the compiler will notice that in
>> abc()'s case, no address is taken so the parameters can be
>> allowed to stay in the registers, while in def()'s case, the
>> compiler will notice that there is an address to be taken for
>> parameter b so it will allocate temporary local auto storage on
>> the stack for b and spill it from the register before the
>> address is taken.  Yes?
>
>Yes.

But just the one, yes?

>> Similarly, in the case for uvw() and xyz()
when the va_start()
>> is applied to one of the parameters, at that point all of the
>> register values for parameters *after* the one specified in
>> va_start() must be spilled into *consecutively addressed* memory
>> locations in such a way that va_arg() can correctly find them.
>> Yes?  (Or they were always on the stack to begin with, I
>> suppose.)
>
>They were probably already on the stack. Since they can't be addressed
>directly, and their number is unlimited, it would make little sense
>to load them into registers.

Indeed.  But my question was more about the case where
va_start() is applied to, say, 'a' or 'b', rather than the
usual
'c'.  The compiler would need to spill all of them at that point
and after, in the parameter list, it seems to me.

Jon

On Tue, 30 Dec 2003 00:55:42 -0000, you wrote:

>> > /* uvw() uses va_start() on parameter a
*/
>> > void uvw (int a, int b, int c, ...);
>
>> > /* xyz() uses va_start() on parameter c */
>> > void xyz (int a, int b, int c, ...);
>
>> > Similarly, in the case for uvw() and xyz() when the va_start()
>> > is applied to one of the parameters, at that point all of the
>> > register values for parameters *after* the one specified in
>> > va_start() must be spilled into *consecutively addressed* memory
>> > locations in such a way that va_arg() can correctly find them.
>> > Yes?  (Or they were always on the stack to begin with, I
>> > suppose.)
>> 
>> They were probably already on the stack. Since they can't be 
>addressed
>> directly, and their number is unlimited, it would make little sense
>> to load them into registers.
>
>Yes, in any implementation I've seen, the caller stores 
>the "placeholder" c and all ... parameters on the stack.
>I think there is no other way, because it's impossible to know what 
>is passed for ... at compile time. The application has to walk 
>through the parameter list at runtime, depending on a format string 
>for example. As you can pass short, long, double etc, the parameters 
>might not even fit into a register.

Yes, this I knew.  But one thing I was (and am) curious about is
if the compilers can find a way to "wait" to decide these things
until the va_start() is actually applied in the function before
spilling.  I can't think of a good way.

Also, I'm confused a little by the standard.  It seems to
suggest in one place that va_start() can only be applied to the
last parameter, but in the following example on the next page
seems to contradict this assertion:

  #include <stdarg.h>
  #define MAXARGS 31

  void f3(int n_ptrs, int f4_after, ...) {
    va_list ap, ap_save;
    char *array[MAXARGS];
    int ptr_no= 0;

      if (n_ptrs > MAXARGS)
        n_ptrs= MAXARGS;
      va_start(ap, n_ptrs);
      while (ptr_no < n_ptrs) {
        array[ptr_no++]= va_arg(ap, char *);
        if (ptr_no == f4_after)
          va_copy(ap_save, ap);
      }
      va_end(ap);
      f2(n_ptrs, array);

      // Now process the saved copy.
      n_ptrs -= f4_after;
      ptr_no = 0;
      while (ptr_no < n_ptrs)
        array[ptr_no++] = va_arg(ap_save, char *);
      va_end(ap_save);
      f4(n_ptrs, array);
  }

Notice that va_start() is applied to the first parameter in this
case and not the second (last, or rightmost) one.

If the example is right, it seems to me that the compiler *must*
pass all of the parameters on the stack, including the first
ones, since va_start() can be applied to any of them and they
will need some kind of consecutive address mechanism.  Putting
any of them in registers and delaying the spill would complicate
this, greatly, I think.  So I believe everything must be
stacked.

But then, I'm unclear about the example in the standard versus
some of the text on the prior page.  So I'll just leave the
question out there for more knowledgable people than me to
answer.

>And it's even possible to take the pointer to
the parameter list and 
>pass it to another function (like vsprintf). So the "..."
parameters 
>must be all on the stack, otherwise the called function would have to 
>build a list in memory which contains also the "..." parameters
that 
>were passed in registers.

Yes.  I'm thinking this way.

>As for uvw(): I wouldn't use va_start() on
parameter a, because the 
>compiler might be free to pass a and b in registers. The caller 
>doesn't know where the called function starts parameter scanning, and 
>if it passes a and b in registers, va_start(a) will certainly fail. 
>The only solution to this would be to pass all parameters on the 
>stack as soon as there is a "...".
>IIRC, ANSI states that va_start() has to be used with the last 
>parameter before the "...", but I'm not sure (maybe Rolf F.
knows 
>better ;-)

Well, the example in the standard seems to differ.  But then,
I'm just curious about what to make of it all.

Jon

On Mon, 29 Dec 2003 17:19:36 -0800, I wrote:

><snip>
>But it's also legal to use a parameter prior to the last
>one before the placeholder.  An example of this, in fact, is in
>the more recent standard.  So it's not just the last one which
>may be used with va_start().
><snip>

Please note my example provided in another recent post and my
stated confusion about this detail there.

Jon

>   #include <stdarg.h>
>   #define MAXARGS 31
> 
>   void f3(int n_ptrs, int f4_after, ...) {
>     va_list ap, ap_save;
>     char *array[MAXARGS];
>     int ptr_no= 0;
> 
>       if (n_ptrs > MAXARGS)
>         n_ptrs= MAXARGS;
>       va_start(ap, n_ptrs);
>       while (ptr_no < n_ptrs) {
>         array[ptr_no++]= va_arg(ap, char *);
>         if (ptr_no == f4_after)
>           va_copy(ap_save, ap);
>       }
>       va_end(ap);
>       f2(n_ptrs, array);
> 
>       // Now process the saved copy.
>       n_ptrs -= f4_after;
>       ptr_no = 0;
>       while (ptr_no < n_ptrs)
>         array[ptr_no++] = va_arg(ap_save, char *);
>       va_end(ap_save);
>       f4(n_ptrs, array);
>   }
> 
> Notice that va_start() is applied to the first parameter in this
> case and not the second (last, or rightmost) one.
> 
> If the example is right, it seems to me that the compiler *must*
> pass all of the parameters on the stack, including the first
> ones, since va_start() can be applied to any of them and they
> will need some kind of consecutive address mechanism.  Putting
> any of them in registers and delaying the spill would complicate
> this, greatly, I think.  So I believe everything must be
> stacked.

This example confuses me too. From anything I know, the 1st va_arg() 
will in the best case return the address of f4_after to be stored in 
array[0]. I don't think this is what's intended.
If n_ptrs is passed in a register, the whole thing is likely to fail 
completely.
Because of this (and because it's 3:15 AM here) I assume the example 
is wrong and hope one of the compiler writers here can clarify this.



On Tue, Dec 30, 2003 at 12:55:42AM -0000, Wolfgang Reich wrote:
> Yes, in any implementation I've seen, the
caller stores 
> the "placeholder" c and all ... parameters on the stack.

It's not actually necessary to pass the placeholder on the
stack. It's often done that way (and the ANSI va_start()
macro requires the placeholder as an argument) but there
are other ways.


-- 
Clyde Stubbs                     |            HI-TECH Software
Email: clyde@clyd...          |          Phone            Fax
WWW:   http://www.htsoft.com/    | USA: (408) 490 2885  (408) 490 2885
PGP:   finger clyde@clyd...   | AUS: +61 7 3552 7777 +61 7 3552 7778
---
HI-TECH C: compiling the real world.

On Mon, Dec 29, 2003 at 05:19:36PM -0800, Jonathan Kirwan wrote:
> Okay.  But it's also legal to use a parameter
prior to the last
> one before the placeholder.  An example of this, in fact, is in

Well, it's not legal. The text is quite specific, the example you cite is
clearly
wrong. If you look at the foreword to the standard, it states that examples
are for information only, not part of the standard.

Clyde


-- 
Clyde Stubbs                     |            HI-TECH Software
Email: clyde@clyd...          |          Phone            Fax
WWW:   http://www.htsoft.com/    | USA: (408) 490 2885  (408) 490 2885
PGP:   finger clyde@clyd...   | AUS: +61 7 3552 7777 +61 7 3552 7778
---
HI-TECH C: compiling the real world.

On Tue, 30 Dec 2003 14:27:45 +1000, Clyde wrote:

>On Mon, Dec 29, 2003 at 05:19:36PM -0800, Jonathan
Kirwan wrote:
>> Okay.  But it's also legal to use a parameter prior to the last
>> one before the placeholder.  An example of this, in fact, is in
>
>Well, it's not legal. The text is quite specific, the example you cite
is clearly
>wrong. If you look at the foreword to the standard, it states that examples
>are for information only, not part of the standard.

Ah.  So it's no more than that?  Thanks, then.  It sure had be
worried a bit.

Jon


Memfault Beyond the Launch