Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

calling convention stdcalll and cdecl call

102 views
Skip to first unread message

George

unread,
Jul 17, 2008, 5:26:06 AM7/17/08
to
Hello everyone,


Both stdcall and cdecl calling convention could support variable input
parameters? Is that correct?

(I think stdcall is using RET N and cdecl is using ESP - N, so both are
capable to handle variable number of input parameter, like printf?)

BTW: I have this question because I have something in mind that only one of
them supports variable number of parameters, but after reading assembly
language code, I think both of them are able to support this feature?


thanks in advance,
George

Alf P. Steinbach

unread,
Jul 17, 2008, 6:36:44 AM7/17/08
to
* George:

both are based on passing arguments on stack.

cdecl pushes right to left, and caller cleans up.

stdcall, as far as i recall, pushes left to right, and called func cleans up.

thus with cdecl you can have an argument list descriptor as first argument (a la
printf), whereas with stdcall you can have it as last argument, but as far as i
know there's no library support for the latter.

anyway the only reason to dabble in this would be to interface to existing
braindead code that uses variable argument list.


cheer, & hth.,

- alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Igor Tandetnik

unread,
Jul 17, 2008, 8:03:40 AM7/17/08
to
"George" <Geo...@discussions.microsoft.com> wrote in message
news:2CC27965-06FB-4E77...@microsoft.com

> Both stdcall and cdecl calling convention could support variable input
> parameters? Is that correct?

No. Variadic functions must use cdecl calling convention.

> (I think stdcall is using RET N and cdecl is using ESP - N, so both
> are capable to handle variable number of input parameter, like
> printf?)

I have no idea what "RET N" or "ESP - N" is supposed to mean.

With stdcall, the function is responsible for removing its parameters
from the stack. To do this, it must know how many parameters there are,
and thus cannot take variable number of parameters.

With cdecl, the caller is responsible for removing parameters after the
called function returns. The caller of course knows how many parameters
it has just pushed on the stack, so there's no problem removing them.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


George

unread,
Jul 18, 2008, 1:44:07 AM7/18/08
to
Thanks Alf,


1.

> thus with cdecl you can have an argument list descriptor as first argument (a la
> printf), whereas with stdcall you can have it as last argument, but as far as i
> know there's no library support for the latter.

The argument list descriptor you mean the string "My name is: %s, and my ID
is %d" in function call printf ("My name is: %s, and my ID is %d", "George",
123)?

2.

What is the root cause why stdcall does not support variable number of input
parameter? Maybe from generated assembly, we could find out?

(I think both caller and callee could get # of input parameters from stack
in theory?)


regards,
George

George

unread,
Jul 18, 2008, 1:45:00 AM7/18/08
to
Thanks Igor,


What is the root cause why stdcall does not support variable number of input
parameter? Maybe from generated assembly, we could find out?

(I think both caller and callee could get # of input parameters from stack
in theory?)


regard,
George

Hendrik Schober

unread,
Jul 18, 2008, 3:39:31 AM7/18/08
to
George wrote:
> Thanks Igor,
>
>
> What is the root cause why stdcall does not support variable number of input
> parameter? Maybe from generated assembly, we could find out?

Igor wrote:
> With stdcall, the function is responsible for removing its parameters
> from the stack. To do this, it must know how many parameters there are,
> and thus cannot take variable number of parameters.

> (I think both caller and callee could get # of input parameters from stack
> in theory?)

In practice, however, that would be yet another parameter to push,
which is unnecessary for 99% of all functions. I wouldn't want to
pay for that.

> George

Schobi

Alf P. Steinbach

unread,
Jul 18, 2008, 4:13:59 AM7/18/08
to
* Igor Tandetnik:

> "George" <Geo...@discussions.microsoft.com> wrote in message
> news:2CC27965-06FB-4E77...@microsoft.com
>> Both stdcall and cdecl calling convention could support variable input
>> parameters? Is that correct?
>
> No. Variadic functions must use cdecl calling convention.
>
>> (I think stdcall is using RET N and cdecl is using ESP - N, so both
>> are capable to handle variable number of input parameter, like
>> printf?)
>
> I have no idea what "RET N" or "ESP - N" is supposed to mean.
>
> With stdcall, the function is responsible for removing its parameters
> from the stack. To do this, it must know how many parameters there are,
> and thus cannot take variable number of parameters.

I'm sorry, that's incorrect (counter-example below).

It's only a tool and library limitation.


> With cdecl, the caller is responsible for removing parameters after the
> called function returns. The caller of course knows how many parameters
> it has just pushed on the stack, so there's no problem removing them.

I now see that I remembered incorrectly about pushing order in my reply earlier
in this thread. Happily, as I recall I wrote a disclaimer "as I recall". ;-)


<code file="x.cpp">
#include <iostream>
#include <stdarg.h>

extern "C" void __stdcall display( int x )
{
std::cout << x << std::endl;
}

void _stdcall knurre( int );
void _stdcall knurre( int, int );
void _stdcall knurre( int, int, int );
void _stdcall knurre( int, int, int, int );

int main()
{
knurre( 1, 101 );
knurre( 2, 201, 202 );
knurre( 3, 301, 302, 303 );
}
</code>


<code file="hackity.asm">
.386P
.model FLAT

_TEXT SEGMENT
EXTRN _display@4:NEAR
display EQU _display@4

PUBLIC ?knurre@@YGXH@Z ; (int)
PUBLIC ?knurre@@YGXHH@Z ; (int, int)
PUBLIC ?knurre@@YGXHHH@Z ; (int, int, int)
PUBLIC ?knurre@@YGXHHHH@Z ; (int, int, int, int)

?knurre@@YGXH@Z:
?knurre@@YGXHH@Z:
?knurre@@YGXHHH@Z:
?knurre@@YGXHHHH@Z:
_knurre PROC NEAR
push ebp
mov ebp, esp

mov ecx, [esp+8]
and ecx, ecx
jz finito

displayLoop:
mov eax, [esp+4*ecx+8]
push ecx
push eax
call _display@4
pop ecx
dec ecx
jnz displayLoop

finito:
mov eax, 1234
mov esp, ebp
pop ebp
pop ebx ; Return address
pop ecx
shl ecx, 2
add esp, ecx
jmp ebx
_knurre ENDP

_TEXT ENDS
END
</code>

<output>
101
202
201
303
302
301
</output>

As you can see at the assembly language level (apologies for novice style code,
it's been a long time!) there's no problem. The main problem with the above was
to get Visual C++ to accept the stdcall vararg routine, not changing the calling
convention to cdecl, which it does when it sees "...". For that, had to declare
the function with all actually used signatures -- so it's just a tool problem,
and for that matter, also library problem, that there's no support.


Cheers, & hth.,

- Alf

Igor Tandetnik

unread,
Jul 18, 2008, 8:55:47 AM7/18/08
to
"George" <Geo...@discussions.microsoft.com> wrote in message
news:22A8AAA7-3D03-474F...@microsoft.com

I'm curious about this theory of yours. Show me how a callee can
determine the size of a variable list of parameters.

Igor Tandetnik

unread,
Jul 18, 2008, 9:12:43 AM7/18/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:XMudnXxazN5UzR3V...@posted.comnet
> * Igor Tandetnik:

>> With stdcall, the function is responsible for removing its parameters
>> from the stack. To do this, it must know how many parameters there
>> are, and thus cannot take variable number of parameters.
>
> I'm sorry, that's incorrect (counter-example below).

Your example uses additional domain-specific information that the
compiler doesn't have, in general: that the first parameter specifies
the total number of remaining parameters, and that all parameters are
the same size.

Or are you suggesting that the caller should pass the total size of all
parameters as a hidden first parameter? That defeats the whole point of
__stdcall convention, which is to shave one machine instruction off the
call site. If you are willing to push the size of parameters onto the
stack before the call, you can just as well update ESP after the call
instead - at which point you are happily back in __cdecl land.

Alex Blekhman

unread,
Jul 18, 2008, 9:19:50 AM7/18/08
to
"Alf P. Steinbach" wrote:
> As you can see at the assembly language level [...] there's no
> problem. [...] so it's just a tool problem, and for that matter,
> also library problem, that there's no support.

I think there is a problem at the assembly language level.
Actually, it is the __stdcall calling convention itself is a
problem.

You have to pass a number of actual parameters to the collee.
Otherwise there is no way to clean the stack from within a
function. This is inherent limitation of __stdcall calling
convention no matter what tool or library you may use. __cdecl
delegates this problem to the caller, so there is nothing to wory
about for the function itself.

Alex


Alf P. Steinbach

unread,
Jul 18, 2008, 1:02:57 PM7/18/08
to
* Igor Tandetnik:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:XMudnXxazN5UzR3V...@posted.comnet
>> * Igor Tandetnik:
>>> With stdcall, the function is responsible for removing its parameters
>>> from the stack. To do this, it must know how many parameters there
>>> are, and thus cannot take variable number of parameters.
>> I'm sorry, that's incorrect (counter-example below).
>
> Your example uses additional domain-specific information that the
> compiler doesn't have, in general: that the first parameter specifies
> the total number of remaining parameters,

Are you of the impression that a vararg routine can access its arguments without
some information about them?


> and that all parameters are
> the same size.

I'm sorry, but that's incorrect.


> Or are you suggesting that the caller should pass the total size of all
> parameters as a hidden first parameter?

That is how it has worked on at least one OS, so it's not a bad idea, but
neither it is a required way to do it.


> That defeats the whole point of
> __stdcall convention, which is to shave one machine instruction off the
> call site. If you are willing to push the size of parameters onto the
> stack before the call, you can just as well update ESP after the call
> instead - at which point you are happily back in __cdecl land.

I'm sorry, but there's no connection between premise and conclusion, and the
conclusion is wrong (e.g. printf is a counter-example).

It would be nice if you'd care to think before posting, but I'm sure this all
babble comes from a wish to not have to admit being wrong.


Cheers,

Igor Tandetnik

unread,
Jul 18, 2008, 2:11:10 PM7/18/08
to
Alf P. Steinbach <al...@start.no> wrote:
> * Igor Tandetnik:
>> "Alf P. Steinbach" <al...@start.no> wrote in message
>> news:XMudnXxazN5UzR3V...@posted.comnet
>>> * Igor Tandetnik:
>>>> With stdcall, the function is responsible for removing its
>>>> parameters from the stack. To do this, it must know how many
>>>> parameters there are, and thus cannot take variable number of
>>>> parameters.
>>> I'm sorry, that's incorrect (counter-example below).
>>
>> Your example uses additional domain-specific information that the
>> compiler doesn't have, in general: that the first parameter specifies
>> the total number of remaining parameters,
>
> Are you of the impression that a vararg routine can access its
> arguments without some information about them?

No, but the precise way the routine acheives that is not known to the
compiler.

>> Or are you suggesting that the caller should pass the total size of
>> all parameters as a hidden first parameter?
>
> That is how it has worked on at least one OS, so it's not a bad idea,
> but neither it is a required way to do it.

What other ways would you suggest to allow the callee to clean up the
stack space occupied by a variable number of parameters? Preferably ways
that a compiler could apply mechanically, without having to understand
the meaning of individual parameters (like having to parse printf-like
format string).

>> That defeats the whole point of
>> __stdcall convention, which is to shave one machine instruction off
>> the call site. If you are willing to push the size of parameters
>> onto the stack before the call, you can just as well update ESP
>> after the call instead - at which point you are happily back in
>> __cdecl land.
>
> I'm sorry, but there's no connection between premise and conclusion,
> and the conclusion is wrong (e.g. printf is a counter-example).

printf is a counterexample of what? It uses cdecl convention, where the
caller is responsible for clearing up the stack. Consider also:

printf("%d", 1, 2);

How would your hypothetical stdcall printf know to remove 12 bytes worth
of parameters, rather than 8?


Let's put it this way. Yes, it is possible to design a calling
convention that a) allows for variable number of arguments, and b)
requires the callee to clean the stack. All you have to do is to have
the caller set up and pass sufficient additional information to the
callee (e.g. total size of all arguments).

However, it's not clear _why_ you would want such a thing. It has no
advantage over __cdecl - you still need an extra machine instruction or
two at every call site to set up this additional information (which is
the problem __stdcall was designed to solve). And, it complicates the
callee, too - witness the dance you had to perform in your assembly code
to simulate RET instruction (where __cdecl function would just have
plain RET, and __stdcall one would have RET n for some constant n).

You complained that tools and libraries don't support such a calling
convention. This is the reason why - this convention doesn't make much
sense. It doesn't improve in any way on those already supported, and in
fact makes things (slightly) worse. Just because something is, in
principle, possible, doesn't necessarily mean it's a good idea.

Alf P. Steinbach

unread,
Jul 18, 2008, 2:53:59 PM7/18/08
to
* Igor Tandetnik:

>
> printf is a counterexample of what? It uses cdecl convention, where the
> caller is responsible for clearing up the stack. Consider also:
>
> printf("%d", 1, 2);
>
> How would your hypothetical stdcall printf know to remove 12 bytes worth
> of parameters, rather than 8?

I think you're stupid.

Igor Tandetnik

unread,
Jul 18, 2008, 3:12:04 PM7/18/08
to
Alf P. Steinbach <al...@start.no> wrote:
> * Igor Tandetnik:
>>
>> printf is a counterexample of what? It uses cdecl convention, where
>> the caller is responsible for clearing up the stack. Consider also:
>>
>> printf("%d", 1, 2);
>>
>> How would your hypothetical stdcall printf know to remove 12 bytes
>> worth of parameters, rather than 8?
>
> I think you're stupid.

That's what kids on the playground say when they don't have any
arguments. Have a nice day.

Alf P. Steinbach

unread,
Jul 18, 2008, 3:30:30 PM7/18/08
to
* Igor Tandetnik:

> Alf P. Steinbach <al...@start.no> wrote:
>> * Igor Tandetnik:
>>> printf is a counterexample of what? It uses cdecl convention, where
>>> the caller is responsible for clearing up the stack. Consider also:
>>>
>>> printf("%d", 1, 2);
>>>
>>> How would your hypothetical stdcall printf know to remove 12 bytes
>>> worth of parameters, rather than 8?
>> I think you're stupid.
>
> That's what kids on the playground say when they don't have any
> arguments. Have a nice day.

Or perhaps you're just weaseling, who knows.

For what it's worth the above example (1) has undefined behavior, (2) states
that I somehow have ownership of an addressing convention, which is incorrect
and misleading and just idiotic, but typical weaseling rhetoric, and (3) asks a
question you already got complete demonstration code for, and based on that
example you yourself pointed out one possible answer to that question.

It shouldn't be that hard to admit a technical mistake.

But instead you start (1) misrepresenting me, (2) accusing me of "complaining"
etc, and now (3) accusing me of lacking arguments when I refuse to address all
your inane babble, as if such accusations could correct your mistake.

But I think you're simply stupid.


- Alf

Alex Blekhman

unread,
Jul 18, 2008, 3:58:56 PM7/18/08
to
"Alf P. Steinbach" wrote:
> It shouldn't be that hard to admit a technical mistake.

I think it is you who should admit a technical mistake, besides
the fact that you owe an apology to Igor for being plain rude.
Igor's answer is to the point and you failed to address the issue.

Your example proves the very point that Igor and I tried to
convey, but you refused to see. In order to enable __stdcall
function to accept a variable number of parameters you have to
pass this information to the function. This is exactly what you
did. At this point __stdcall is not much different from existing
__cdecl.

Alex


Alf P. Steinbach

unread,
Jul 18, 2008, 4:15:51 PM7/18/08
to
* Alex Blekhman:

> "Alf P. Steinbach" wrote:
>> It shouldn't be that hard to admit a technical mistake.
>
> I think it is you who should admit a technical mistake, besides
> the fact that you owe an apology to Igor for being plain rude.

Igor owes me apology for misrepresenting me, calling me a complainer etc. And
I'm doing him honors by assuming he's stupid rather than weaseling person
employing deception. So he should properly also thank me, after apologizing.


> Igor's answer is to the point and you failed to address the issue.

Igor made a mistake (i.e. not answer and not to the point), I posted working
code that completely addressed the issue (i.e. not failed to address issue but
the complete opposite, I addressed the issue, fully), and what is the point of
posting two such blatantly false assertions in one sentence?


> Your example proves the very point that Igor and I tried to
> convey, but you refused to see.

Igor write "thus cannot take variable number of parameters", which as
demonstrated is *incorrect*, and it is a self-contradiction that I should have
refused to see what I demonstrated. Given the lack of coherence and meaning in
Igor's successive articles I don't know what he originally tried to convey, so
you may be right that he may have meant to convey something wildly different
from what he wrote. But unless he has explained that via some other physical
channel to you, you don't know that either, other than via telepathy.

So, telepathy + self-contradiction in one sentence => a bit overheated, perhaps?

No matter, 'tis OK. :-)


> In order to enable __stdcall
> function to accept a variable number of parameters you have to
> pass this information to the function. This is exactly what you
> did.

That's correct.


> At this point __stdcall is not much different from existing
> __cdecl.

The "not much" means that it's a matter of personal opinion, not something we
can resolve if we should turn out to be of different minds about that. So, not
meaningful. I gather you do agree that there is a difference, no matter size.

Cheers, & hth.

Hendrik Schober

unread,
Jul 18, 2008, 4:13:47 PM7/18/08
to
Alf P. Steinbach wrote:
> [...]

> It would be nice if you'd care to think before posting, but I'm sure this all
> babble comes from a wish to not have to admit being wrong.

I have no idea where this came from and your following answers
convey exactly what above you try to pin on Igor.
So far this had been a very interesting discussion, but now you
stopped arguing and instead resorted to being rude, so that I
can't help but think you ran out of arguments.

> - Alf

Schobi

Igor Tandetnik

unread,
Jul 18, 2008, 4:34:51 PM7/18/08
to
Alf P. Steinbach <al...@start.no> wrote:
> * Igor Tandetnik:
>>>> printf("%d", 1, 2);
>
> For what it's worth the above example (1) has undefined behavior

Not true. C99 standard explicitly covers this case:

7.19.6.1/2 The fprintf function writes output to the stream pointed to
by stream, under control of the string pointed to by format that
specifies how subsequent arguments are converted for output. If there
are insufficient arguments for the format, the behavior is undefined.
*If the format is exhausted while arguments remain, the excess arguments
are evaluated (as always) but are otherwise ignored.* (emphasis mine).

In 7.19.6.3, printf is defined in terms of fprintf.

I don't have C89 standard handy at the moment, but I'm pretty sure it
contains the same wording.

> (2) states that I somehow have ownership of an addressing convention,
> which is incorrect and misleading and just idiotic, but typical
> weaseling rhetoric

What do you mean by "addressing convention"? I'm not familiar with the
term. Since I don't know what it is, I don't make any statements about
its ownership.

All I state is this. The above call is perfectly valid according to the
standard, so a conforming C compiler has to support it. To execute this
call, three arguments have to be pushed onto the stack, and then later
three arguments have to be popped off. Under cdecl calling convention,
both operations are done by the caller, who of course knows how many
arguments there are.

You seem to suggest that it is perfectly reasonable to require the
callee to clean up the stack instead. I'd like to see what mechanism you
propose to make this possible, other than having the caller pass
additional information to the callee, above and beyond the three
arguments (e.g. the total size of all arguments).

If the mechanism you'd like to propose does involve passing additional
information to the function, then I agree with you in that such a
calling convention would indeed be possible. However, I don't see how it
would be an improvement over cdecl. And since cdecl is already happily
supported by existing tools, it is hard to expect compiler authors to
begin supporting something that is worse. What would be the point?

On the other hand, stdcall is an improvement over cdecl, however minor.
It shifts one machine instruction from the call site (where it has to be
repeated for each call) to the callee (where it exists only once per
function, not once per every place in the program where the function is
called). In exchange for this slight efficiency gain, it loses the
ability to support variadic functions.

> and (3) asks a question you already got complete
> demonstration code for, and based on that example you yourself
> pointed out one possible answer to that question.

Right. My point is _not_ that the technique your sample demonstrates is
impossible. Just that it's unnecessarily complicated and pointless, and
so it's not at all surprising that it's not supported by mainstream
tools.

Ben Voigt [C++ MVP]

unread,
Jul 18, 2008, 4:57:05 PM7/18/08
to
Igor Tandetnik wrote:
> "George" <Geo...@discussions.microsoft.com> wrote in message
> news:22A8AAA7-3D03-474F...@microsoft.com
>> What is the root cause why stdcall does not support variable number
>> of input parameter? Maybe from generated assembly, we could find out?
>>
>> (I think both caller and callee could get # of input parameters from
>> stack in theory?)
>
> I'm curious about this theory of yours. Show me how a callee can
> determine the size of a variable list of parameters.

By parsing the format string...

Of course, this would cause even more terrible things to happen when the
format string mismatches the parameters than currently happens.

non-printf style functions still need to process all the parameters to do
their work, so they too need some way of identifying how many parameters
will be used.


Liviu

unread,
Jul 18, 2008, 5:01:39 PM7/18/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Igor Tandetnik:
>> Alf P. Steinbach <al...@start.no> wrote:
>>> * Igor Tandetnik:
>>>>
>>>> printf("%d", 1, 2);
>
> For what it's worth the above example (1) has undefined behavior

Nope, behavior is both defined and expected (prints 1, excess arguments
are evaluated but ignored).

You might be thinking at "printf("%d");" but even in that case the
"undefinededness" is particular to printf, and has nothing to do with
calling conventions for variadic functions in general. Didn't follow the
thread in all its glorious detail. but if you are trying to make the
point that a callee can "magically guess" the number of arguments pushed
on the stack by the caller (without hidden parameters or additional
overhead like standard stack "frames"), then that's so obviously wrong.


Alex Blekhman

unread,
Jul 18, 2008, 5:23:01 PM7/18/08
to
"Alf P. Steinbach" wrote

>> Your example proves the very point that Igor and I tried to
>> convey, but you refused to see.
>
> Igor write "thus cannot take variable number of parameters",
> which as demonstrated is *incorrect*, and it is a
> self-contradiction that I should have refused to see what I
> demonstrated.

Oh, come on. What you have demonstrated that a developer can pull
a trick and fool the compiler. You could have written
__declspec(naked) for that matter. What you have written is not
__stdcall anymore, but your own calling convention that supports a
variable number of arguments.

>> At this point __stdcall is not much different from existing
>> __cdecl.
>
> The "not much" means that it's a matter of personal opinion, not
> something we can resolve if we should turn out to be of
> different minds about that.

The "not much" means the following:

1. __stdcall, as it is implemeted today, cannot accept a variable
number of arguments because a function must know a number of
actual arguments at compile time.

2. In order to enable __stdcall to accept a variable number of
arguments we need to change it. This change envolves
a) passing an additional hidden argument to a function,
b) changing a way that a function uses to clean the stack.

3. Making the above changes we eventually have invented a new
calling convention, which does the same job that __cdecl designed
to do and uses the same amount of CPU instructions to make things
work.

So, the question is why do we need to invent this new calling
convention when we have perfectly working __cdecl already?

Let me return to your asm example to demonstrate the point.
Imagine that you need to make a DLL that exports

void __stdcall knurre(...);

function. All you know is that compiler pushes parameters right to
left and you are responsible to clean the stack. It is simply
impossible to do *without changing* the way how the compiler
generates __stdcall calling code.

Alex


Alf P. Steinbach

unread,
Jul 18, 2008, 5:27:52 PM7/18/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote
>> * Igor Tandetnik:
>>> Alf P. Steinbach <al...@start.no> wrote:
>>>> * Igor Tandetnik:
>>>>> printf("%d", 1, 2);
>> For what it's worth the above example (1) has undefined behavior
>
> Nope, behavior is both defined and expected (prints 1, excess arguments
> are evaluated but ignored).

In C99. Thanks, I didn't know. Do you have C89 standard?


> You might be thinking at "printf("%d");" but even in that case the
> "undefinededness" is particular to printf, and has nothing to do with
> calling conventions for variadic functions in general.

It had to do with a minor point that Igor may or may not have been trying to
make, who knows since evidently he doesn't mean what he writes.

But just to address what you say, although unrelated to original context: I'm
sorry, but that's incorrect, i.e. contrary to your claim the behavior of printf
has to do with calling conventions for variadic functions in general.

C99 printf directly supports a functionally limited and generally inefficient
calling convention (C-style) and for printf, makes the case that that convention
doesn't handle, undefined behavior.


> Didn't follow the
> thread in all its glorious detail. but if you are trying to make the
> point that a callee can "magically guess" the number of arguments pushed
> on the stack by the caller (without hidden parameters or additional
> overhead like standard stack "frames"), then that's so obviously wrong.

Yeah, it's clear that you haven't followed the thread. I replied originally to
Igor by posting complete C++ plus assembly language code for an example of
__stdcall variadic arguments. Showing that his claim that it was impossible, was
simply incorrect.

Since then a number of idiotic claims and positions, such as the one you
describe, have been argued against, /as if/ they had been claims I made, plus
that evidently I have been complaining, plus thinking my ways are "better", +++.

I made only one claim, and proved it, that Igor's claim was 100% incorrect. Any
other claims attributed to me are just deception, weaseling. I don't understand
how it can be so hard for some to admit they made an error, but evidently it is.


Cheers,

Alf P. Steinbach

unread,
Jul 18, 2008, 5:48:50 PM7/18/08
to
* Alex Blekhman:

> "Alf P. Steinbach" wrote
>>> Your example proves the very point that Igor and I tried to
>>> convey, but you refused to see.
>> Igor write "thus cannot take variable number of parameters",
>> which as demonstrated is *incorrect*, and it is a
>> self-contradiction that I should have refused to see what I
>> demonstrated.
>
> Oh, come on. What you have demonstrated that a developer can pull
> a trick and fool the compiler. You could have written
> __declspec(naked) for that matter. What you have written is not
> __stdcall anymore, but your own calling convention that supports a
> variable number of arguments.

It's pure __stdcall, as you can see from the C++ interface.

The C++ compiler only sees a set of __stdcall functions.

Hence those functions, by virtue of walking, waddling and quacking and in any
other way behaving just like __stdcall functions, are __stdcall functions.

And since each of them is identically, same address, the same as a single
variadic function, that one is also __stdcall.

If that was too complex an explanation, then think about what property of
__stdcall is violated: there is no such property.

>>> At this point __stdcall is not much different from existing
>>> __cdecl.
>> The "not much" means that it's a matter of personal opinion, not
>> something we can resolve if we should turn out to be of
>> different minds about that.
>
> The "not much" means the following:
>
> 1. __stdcall, as it is implemeted today, cannot accept a variable
> number of arguments because a function must know a number of
> actual arguments at compile time.
>
> 2. In order to enable __stdcall to accept a variable number of
> arguments we need to change it. This change envolves
> a) passing an additional hidden argument to a function,
> b) changing a way that a function uses to clean the stack.

There are more alternatives here.

In particular, one practical one would be where the function calls some library
function that reports the stack usage, based on e.g. printf-style first argument.


> 3. Making the above changes we eventually have invented a new
> calling convention, which does the same job that __cdecl designed
> to do

No it isn't a new calling convention, no it isn't invented by "us" (whoever that
is), and no it doesn't do just the same job as __cdecl, it does a little more.


> and uses the same amount of [executed] CPU instructions to make things
> work.

Actually may uses fewer or more (I'd guess one or two more), but in the case
where a function fits in a cache but calling code doesn't, may be more efficient.


> So, the question is why do we need to invent this new calling
> convention when we have perfectly working __cdecl already?

Whoever the plural "you" are, probably don't need to invent anything.

I just showed that the claim that it was impossible, was totally incorrect.

Your inference that there is a need for this may be correct or not, I dunno.


> Let me return to your asm example to demonstrate the point.
> Imagine that you need to make a DLL that exports
>
> void __stdcall knurre(...);
>
> function. All you know is that compiler pushes parameters right to
> left and you are responsible to clean the stack. It is simply
> impossible to do *without changing* the way how the compiler
> generates __stdcall calling code.

Is the point that if you insist on using a way that doesn't work, it won't work?

Seems a little silly to me. ;-)


Cheers, & hth.,

Liviu

unread,
Jul 18, 2008, 6:22:40 PM7/18/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Igor Tandetnik:
>>>> Alf P. Steinbach <al...@start.no> wrote:
>>>>> * Igor Tandetnik:
>>>>>> printf("%d", 1, 2);
>>> For what it's worth the above example (1) has undefined behavior
>>
>> Nope, behavior is both defined and expected (prints 1, excess
>> arguments
>> are evaluated but ignored).
>
> In C99. Thanks, I didn't know. Do you have C89 standard?

Don't have it handy, but if google's
http://rm-f.net/~orange/devel/specifications/c89-draft.html#4.9.6.1
is to be trusted, then it's the same:

| If there are insufficient arguments for the format, the behavior is

| undefined. If the format is exhausted while arguments remain, the


| excess arguments are evaluated (as always) but are otherwise ignored.

>> You might be thinking at "printf("%d");" but even in that case the


>> "undefinededness" is particular to printf, and has nothing to do with
>> calling conventions for variadic functions in general.
>
> It had to do with a minor point that Igor may or may not have been
> trying to make, who knows since evidently he doesn't mean what he
> writes.

I'll go on a limb here ;-) but I trust Igor meant exactly what he wrote:

| With stdcall, the function is responsible for removing its parameters
| from the stack. To do this, it must know how many parameters there
| are, and thus cannot take variable number of parameters.

> But just to address what you say, although unrelated to original


> context: I'm sorry, but that's incorrect, i.e. contrary to your claim
> the behavior of printf has to do with calling conventions for variadic
> functions in general.
>
> C99 printf directly supports a functionally limited and generally
> inefficient calling convention (C-style) and for printf, makes the
> case that that convention doesn't handle, undefined behavior.

Sorry, but I don't follow. The only undefined behavior mentioned there
is when the caller passes _fewer_ arguments than expected from the
format string, and that's because of printf internals. On the other
hand, the standard explicitly states that excess parameters are
ignored, so it's painfully obvious that printf couldn't possibly clear
the stack after itself based on what the format string says.

Imagine for a moment that you'd be writing the __stdcall printf(const
char *, ...) code. At the point your code would be entered, everything
would look _exactly_ the same when the function is called in either of
these ways:

printf("%d", 1);


printf("%d", 1, 2);

How would you code your printf so that it pops the right number of bytes
off the stack in each case?

> I replied originally to Igor by posting complete C++ plus assembly
> language code for an example of __stdcall variadic arguments.

No. You just provided several overloads of the 'knurre' function which
happened to internally share some assembly code.

If the point of the exercise was to show that passing an additional
"number of params" into a function can allow it to pop the stack itself,
then yes of course it's both possible and trivial. But it's not
__stdcall, since there is no presumption in __stdcall that such a
"number of params" argument must exist.

And, if you proposed a new calling convention which does that, it would
be highly fragile, and something like

knurre(1, 101, 102, 103);

would not only confuse the callee, but also corrupt the entire stack.

> Showing that his claim that it was impossible, was simply incorrect.

I requoted his claim above. What part exactly do you find incorrect?

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 18, 2008, 6:39:49 PM7/18/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote
>> It had to do with a minor point that Igor may or may not have been
>> trying to make, who knows since evidently he doesn't mean what he
>> writes.
>
> I'll go on a limb here ;-) but I trust Igor meant exactly what he wrote:
>
> | With stdcall, the function is responsible for removing its parameters
> | from the stack. To do this, it must know how many parameters there
> | are, and thus cannot take variable number of parameters.

That was not it. But that was the start of that subthread, yes.


> I requoted his claim above. What part exactly do you find incorrect?

"and thus cannot take variable number of parameters" is incorrect.


Cheers,

- Aldf

Alf P. Steinbach

unread,
Jul 18, 2008, 6:52:50 PM7/18/08
to

See below.


> The only undefined behavior mentioned there
> is when the caller passes _fewer_ arguments than expected from the
> format string, and that's because of printf internals.

No, that's not because of printf internals, that's because with the C calling
convention printf has no way of checking that there are indeed enough arguments.

> On the other
> hand, the standard explicitly states that excess parameters are
> ignored, so it's painfully obvious that printf couldn't possibly clear
> the stack after itself based on what the format string says.

Unfortunately, yes.


> Imagine for a moment that you'd be writing the __stdcall printf(const
> char *, ...) code. At the point your code would be entered, everything
> would look _exactly_ the same when the function is called in either of
> these ways:
>
> printf("%d", 1);
> printf("%d", 1, 2);
>
> How would you code your printf so that it pops the right number of bytes
> off the stack in each case?

No problem. The number of bytes is passed in any way the compiler deems
suitable, since printf is declared with "...". This is the same as the compiler
is free to use e.g. RVO, and really whatever it wants; you'd not say that a
stdcall function that the compiler implements with RVO is not stdcall?


>> I replied originally to Igor by posting complete C++ plus assembly
>> language code for an example of __stdcall variadic arguments.
>
> No. You just provided several overloads of the 'knurre' function which
> happened to internally share some assembly code.

So? "You just provided a car-minus-wheels plus wheels attached". How is that the
reality different from a car, just by your description being different and
focused on irrelevant details?


> If the point of the exercise was to show that passing an additional
> "number of params" into a function can allow it to pop the stack itself,
> then yes of course it's both possible and trivial. But it's not
> __stdcall, since there is no presumption in __stdcall that such a
> "number of params" argument must exist.

The __stdcall convention does not address what arguments are used for.

It's meaningless to say that void foo(char ch), when compiled as stdcall, isn't
stdcall just because that argument is used for output or whatever, and that that
argument usage is not prescribed by stdcall.

And it's meaningless to say that an RVO-optimized function isn't stdcall (an
RVO-optimized function may have an extra hidden compiler-generated argument).
However by your reasoning it can't be. I don't really think you believe this
idiocy yourself, I think you're just putting it forward as rhetoric.


Cheers, & hth.,

Igor Tandetnik

unread,
Jul 18, 2008, 7:20:42 PM7/18/08
to
Alf P. Steinbach <al...@start.no> wrote:
> * Alex Blekhman:
>> "Alf P. Steinbach" wrote
>>>> Your example proves the very point that Igor and I tried to
>>>> convey, but you refused to see.
>>> Igor write "thus cannot take variable number of parameters",
>>> which as demonstrated is *incorrect*, and it is a
>>> self-contradiction that I should have refused to see what I
>>> demonstrated.
>>
>> Oh, come on. What you have demonstrated that a developer can pull
>> a trick and fool the compiler. You could have written
>> __declspec(naked) for that matter. What you have written is not
>> __stdcall anymore, but your own calling convention that supports a
>> variable number of arguments.
>
> It's pure __stdcall, as you can see from the C++ interface.

No it's not, really. You have effectively defined a new calling
convention - let's call it alfcall - where the caller passes the number
or size of the argument list to the callee, and the callee is
responsible for cleaning the stack. alfcall is different from both cdecl
and stdcall.

You then go to some lengths to simulate alfcall using existing
mechanisms. On the caller side, you have to define multiple function
signatures for what is supposed to be one variadic function, and you
have to explicitly supply an extra argument that contains the number of
remaining arguments. On the callee side, you can't use C or C++ at all,
but have to implement it in assembly.

Consider an example. Suppose I have a function

void f(int x, int y) {}

and I call it like this: f(1, 2). Let's see what the call site and the
function body would look like if the function were declared cdecl,
stdcall and alfcall.

1) cdecl
; call site
push 2
push 1
call f
add esp, 8 ; pop parameters

; function body
ret

2) stdcall
; call site
push 2
push 1
call f

; function body
ret 8 ; pop parameters

3) alfcall
; call site
push 2
push 1
push 8 ; total size of arguments
call f

; function body
pop ebx ; return address
pop ecx ; size of arguments
add esp, ecx; pop arguments
jmp ebx ; return


Compared to cdecl, call site for alfcall convention has the same number
of machine instructions, but the function epilogue is larger. Contrast
with stdcall, where the call site is one instruction smaller (which was
the goal of the exercise), and the function epilogue is slightly larger.

So, while it is possible to implement something like alfcall calling
convention, it's not clear why one should bother, given that it's
strictly worse than cdecl.

> The C++ compiler only sees a set of __stdcall functions.

But wasn't the goal of the exercise to have the C++ compiler see just
one variadic function with __stdcall convention? Defining a set of
__stdcall functions, each with a fixed signature, is not particularly
interesting. Requiring the developer to count parameters and pass the
count explicitly probably won't help adoption either. Neither would
having to write the function itself in assembly.

I assumed that your sample was intended to demonstrate that a compiler
could implement alfcall calling convention natively, and that it would
be a good idea for it to do so. I agree with he former, but disagree
with the latter.

> Hence those functions, by virtue of walking, waddling and quacking
> and in any other way behaving just like __stdcall functions, are
> __stdcall functions.

No they do not, and they are not. A regular __stdcall function doesn't
require me to explicitly pass the number of arguments as its first
parameter. Yours does: if I don't specify the first parameter correctly,
it doesn't work. Your function places constraints on the caller above
and beyond those specified by __stdcall convention. Thus, it is no
longer a __stdcall calling convention, but something else.

> And since each of them is identically, same address, the same as a
> single variadic function, that one is also __stdcall.

Can I call this variadic function with 10 arguments? With 100?

> If that was too complex an explanation, then think about what
> property of __stdcall is violated: there is no such property.

Not having to explicitly pass the number of parameters.

> In particular, one practical one would be where the function calls
> some library function that reports the stack usage, based on e.g.
> printf-style first argument.

In addition to being theoretically possible, it also needs to be
practical. What benefits are there to doing it this way? In what way is
this better than what exists now?

>> 3. Making the above changes we eventually have invented a new
>> calling convention, which does the same job that __cdecl designed
>> to do
>
> No it isn't a new calling convention, no it isn't invented by "us"
> (whoever that is), and no it doesn't do just the same job as __cdecl,
> it does a little more.

In your example with printf-style string, the caller would have to
prepare this string and pass it to the callee. How does this not
constitute a new calling convention? What existing convention already
does this?

Liviu

unread,
Jul 18, 2008, 7:44:04 PM7/18/08
to

"Alf P. Steinbach" <al...@start.no> wrote in message
news:FrydnQFAxMIkhhzV...@posted.comnet...

>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> It had to do with a minor point that Igor may or may not have been
>>> trying to make, who knows since evidently he doesn't mean what he
>>> writes.
>>
>> I'll go on a limb here ;-) but I trust Igor meant exactly what he
>> wrote:
>>
>> | With stdcall, the function is responsible for removing its
>> | parameters from the stack. To do this, it must know how many
>> | parameters there are, and thus cannot take variable number of
>> | parameters.
>>
>> I requoted his claim above. What part exactly do you find incorrect?
>
> "and thus cannot take variable number of parameters" is incorrect.

Please explain how a function which "must know how many
parameters there are" does, in fact, know that when called with a
variable number thereof.


An example would suffice...

; this is the caller code ........................................

; calling a variadic _stdcall function with 1 argument
push eax
call _printf

; calling the same variadic _stdcall function with 2 arguments
push eax
push eax
call _printf

; ...etc

; please fill in this part ........................................

_printf:
; --- your code here ---
; --- doesn't have to do anything except _not_ corrupt the stack ---


Please note that once you impose additional requirements on the caller
code (pass number of params, set some sort of stack frame, etc) then
that's no longer the __stdcall convention.

Also, you must appreciate that __stdcall is just the C name for a well
defined binary contract between caller and callee. It is not a matter of
compiler, and there doesn't even have to be one involved. I could
technically write assembler, or direct machine bytecodes in debug.exe if
I so wished, and the same calling convention would still apply.

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 18, 2008, 9:24:15 PM7/18/08
to
* Igor Tandetnik:

> Alf P. Steinbach <al...@start.no> wrote:
>> * Alex Blekhman:
>>> "Alf P. Steinbach" wrote
>>>>> Your example proves the very point that Igor and I tried to
>>>>> convey, but you refused to see.
>>>> Igor write "thus cannot take variable number of parameters",
>>>> which as demonstrated is *incorrect*, and it is a
>>>> self-contradiction that I should have refused to see what I
>>>> demonstrated.
>>> Oh, come on. What you have demonstrated that a developer can pull
>>> a trick and fool the compiler. You could have written
>>> __declspec(naked) for that matter. What you have written is not
>>> __stdcall anymore, but your own calling convention that supports a
>>> variable number of arguments.
>> It's pure __stdcall, as you can see from the C++ interface.
>
> No it's not, really. You have effectively defined a new calling
> convention - let's call it alfcall - where the caller passes the number
> or size of the argument list to the callee, and the callee is
> responsible for cleaning the stack. alfcall is different from both cdecl
> and stdcall.

Heya. I'm being nice to you, explaining.

The meaning of arguments does /not/ constitute a calling convention.

Using an argument for something does not change the calling convention.

You might try Wikipedia or something for simple explanations of what a calling
convention is.

Do you want me to find you a link?


> You then go to some lengths to simulate alfcall using existing
> mechanisms. On the caller side, you have to define multiple function
> signatures for what is supposed to be one variadic function, and you
> have to explicitly supply an extra argument that contains the number of
> remaining arguments. On the callee side, you can't use C or C++ at all,
> but have to implement it in assembly.

Something that isn't supported by the tools can't be done directly by the tools.

That was the original point of the exercise, to show that (do you want me to
quote myself? if so, can do).

So in a sense you're on the right track, but I fear that this recognition of
pertinent features of the example is not accompanied by proper understanding.
I'm willing to help you by finding you links with explanations of calling
conventions etc. (note: might take time). Or I can teach you directly here if
you want that, even though I think that would be a bit off-topic for the group.

Alf P. Steinbach

unread,
Jul 18, 2008, 9:29:34 PM7/18/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:FrydnQFAxMIkhhzV...@posted.comnet...
>> * Liviu:
>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>> It had to do with a minor point that Igor may or may not have been
>>>> trying to make, who knows since evidently he doesn't mean what he
>>>> writes.
>>> I'll go on a limb here ;-) but I trust Igor meant exactly what he
>>> wrote:
>>>
>>> | With stdcall, the function is responsible for removing its
>>> | parameters from the stack. To do this, it must know how many
>>> | parameters there are, and thus cannot take variable number of
>>> | parameters.
>>>
>>> I requoted his claim above. What part exactly do you find incorrect?
>> "and thus cannot take variable number of parameters" is incorrect.
>
> Please explain how a function which "must know how many
> parameters there are" does, in fact, know that when called with a
> variable number thereof.
>
>
> An example would suffice...

You have already C++ and assembly language example, from earlier in this thread.


> ; this is the caller code ........................................
>
> ; calling a variadic _stdcall function with 1 argument
> push eax
> call _printf
>
> ; calling the same variadic _stdcall function with 2 arguments
> push eax
> push eax
> call _printf
>
> ; ...etc
>
> ; please fill in this part ........................................
>
> _printf:
> ; --- your code here ---
> ; --- doesn't have to do anything except _not_ corrupt the stack ---
>
>
> Please note that once you impose additional requirements on the caller
> code (pass number of params, set some sort of stack frame, etc) then
> that's no longer the __stdcall convention.
>
> Also, you must appreciate that __stdcall is just the C name for a well
> defined binary contract between caller and callee. It is not a matter of
> compiler, and there doesn't even have to be one involved. I could
> technically write assembler, or direct machine bytecodes in debug.exe if
> I so wished, and the same calling convention would still apply.

You asked this earlier and had it explained to you, in message
<mOednStCze1YgxzV...@posted.comnet>. Quoting from that message:

[Quoted from earlier message:]


>> Imagine for a moment that you'd be writing the __stdcall printf(const
>> char *, ...) code. At the point your code would be entered, everything
>> would look _exactly_ the same when the function is called in either of
>> these ways:
>>
>> printf("%d", 1);
>> printf("%d", 1, 2);
>>
>> How would you code your printf so that it pops the right number of bytes
>> off the stack in each case?
>

> No problem. The number of bytes is passed in any way the compiler deems suitable

Cheers, & hth.,

- Alf

--

Alf P. Steinbach

unread,
Jul 18, 2008, 9:43:13 PM7/18/08
to
* Liviu:

> Please note that once you impose additional requirements on the caller
> code (pass number of params, set some sort of stack frame, etc) then
> that's no longer the __stdcall convention.

Sorry, I didn't see that. That's too stupid for my limited English vocabulary.
Do you think we're engaged in some children's "guess my riddle"?

Anyways, we were discussing a statement that stdcall convention *could not*
support varargs: "thus, ...".

Then it's completely false and misleading to snub any counter-example by "that's
not stdcall as I know it". That argument is "I define stdcall as a convention
that can't support vargarg, therefore, it can't". Do you see how stupid that
is? Well, probably not. But it is, it's below a snail's level of intelligence.


Cheers,

- Alf

Liviu

unread,
Jul 18, 2008, 9:48:47 PM7/18/08
to

"Alf P. Steinbach" <al...@start.no> wrote in message
news:-b6dnbW47NIf3hzV...@posted.comnet...

>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote in message
>> news:FrydnQFAxMIkhhzV...@posted.comnet...
>>> * Liviu:
>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>> It had to do with a minor point that Igor may or may not have been
>>>>> trying to make, who knows since evidently he doesn't mean what he
>>>>> writes.
>>>> I'll go on a limb here ;-) but I trust Igor meant exactly what he
>>>> wrote:
>>>>
>>>> | With stdcall, the function is responsible for removing its
>>>> | parameters from the stack. To do this, it must know how many
>>>> | parameters there are, and thus cannot take variable number of
>>>> | parameters.
>>>>
>>>> I requoted his claim above. What part exactly do you find
>>>> incorrect?
>>> "and thus cannot take variable number of parameters" is incorrect.
>>
>> Please explain how a function which "must know how many
>> parameters there are" does, in fact, know that when called with a
>> variable number thereof.
>>
>> An example would suffice...
>
> You have already C++

You don't decide who's calling you, unless you target a very narrow,
closed, self contained world.

> and assembly language example, from earlier in this thread.

I pasted it under "_printf:" below and it failed miserably ;-)

If you have one which actually works in the quoted context, then by all
means please surprise all the rest of us and post it.

Let me see if I get this right... Your example is then proposing a
calling convention which current compilers don't support, but could
conceivably be made to work with cooperation from next generation
compilers when issuing function calls between themselves. OK, fine, no
argument there. But then (a) why refer to it as __stdcall, which is a
different, established calling convention, and (b) what bearing has it
at all on Igor's statement you pretend to dispute.

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 18, 2008, 10:08:26 PM7/18/08
to

You were unable to ride a bicycle and blame the bicycle. It doesn't work. Well,
that's not the only possible conclusion, you know.

No, it is not.

It demonstrated that an existing calling convention could support varargs,
actually that it supports it better than the conventional one for that, and that
tool support for that was lacking.

Not that we need that tool support, as far as I can see.


> but could
> conceivably be made to work with cooperation from next generation
> compilers when issuing function calls between themselves. OK, fine, no
> argument there. But then (a) why refer to it as __stdcall, which is a
> different, established calling convention,

It is not.

Do you know what __stdcall is?

It's a combination of a few things.

At the machine code level, that arguments are pushed right to left, and that the
callee adjusts the stack pointer back when returning. This my example did.

At a higher level, that a certain name mangling, defined by the specific
compiler (it varies between compilers), is used. Note that at this level the
meaning of stdcall varies between compilers. My example did employ this name
mangling, but by doing so also indicated that that particular compiler's name
mangling scheme failed to accomodate variable number of arguments -- at least
as far as I could see, and I'm not going to investigate that further.

At no point did my example violate the contract of stdcall, which was evidenced
by Visual C++ being able to call the function correctly.


> and (b) what bearing has it
> at all on Igor's statement you pretend to dispute.

I'm now wondering whether I'm actually talking to Igor under an alias: two just
about equally thickheaded persons with the same blind zone.

But anyway, Igor maintained that stdcall could not accomodate variable number of
arguments.

That was incorrect.

Liviu

unread,
Jul 18, 2008, 10:15:57 PM7/18/08
to

"Alf P. Steinbach" <al...@start.no> wrote in message
news:sfKdnW8tnNsu2xzV...@posted.comnet...

>* Liviu:
>> Please note that once you impose additional requirements on the
>> caller
>> code (pass number of params, set some sort of stack frame, etc) then
>> that's no longer the __stdcall convention.
>
> Sorry, I didn't see that. That's too stupid for my limited English
> vocabulary. Do you think we're engaged in some children's "guess my
> riddle"?
>
> Anyways, we were discussing a statement that stdcall convention *could
> not* support varargs: "thus, ...".

Let's just rephrase that to "__stdcall can technically call any random
memory address, including that of a variadic function, except it's not
safe to do so and will crash more often than not" ;-)

> Then it's completely false and misleading to snub any counter-example
> by "that's not stdcall as I know it".

You forgot a comma after stdcall ;-)

> That argument is "I define stdcall as a convention that can't support
> vargarg, therefore, it can't". Do you see how stupid that is? Well,
> probably not. But it is, it's below a snail's level of intelligence.

It's been a long day, and the OP's question was fully answered long ago.
No reason to waste your time, indeed.

Cheers,
Liviu


Alexander Grigoriev

unread,
Jul 18, 2008, 10:46:04 PM7/18/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:eaX9oGQ6...@TK2MSFTNGP04.phx.gbl...

>
> What other ways would you suggest to allow the callee to clean up the
> stack space occupied by a variable number of parameters? Preferably ways
> that a compiler could apply mechanically, without having to understand the
> meaning of individual parameters (like having to parse printf-like format
> string).
>

Here comes another thing of callng convention: who is destroying
non-reference non-POD arguments of a function. Such objects are destroyed
upon function return (according to ISO14882 1.9/13, a function call is full
expression). These should be destroyed before the stack pointer is adjusted
back.

And I'm not talking about variadic functions.


Igor Tandetnik

unread,
Jul 18, 2008, 11:43:38 PM7/18/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:s76dnfnYGKVeUR3V...@posted.comnet
> * Igor Tandetnik:

>> "Alf P. Steinbach" <al...@start.no> wrote in message
>> news:XMudnXxazN5UzR3V...@posted.comnet
>>> * Igor Tandetnik:

>>>> With stdcall, the function is responsible for removing its
>>>> parameters from the stack. To do this, it must know how many
>>>> parameters there are, and thus cannot take variable number of
>>>> parameters.
>>> I'm sorry, that's incorrect (counter-example below).
>>
>> Your example uses additional domain-specific information that the
>> compiler doesn't have, in general: that the first parameter specifies
>> the total number of remaining parameters,
>
> Are you of the impression that a vararg routine can access its
> arguments without some information about them?

I think I finally understand what you are trying to say. Please correct
me if I'm wrong. It goes like this.

A variadic function needs some way to know which parameters it is given.
It could be a printf-like format string, a counter passed as the first
parameter, or perhaps some sentinel value terminating the sequence. If
the compiler can somehow figure out how the function accesses its
parameters (presumably by semantic analysis of its source code), it can
figure out how many there are, and pop the stack correctly without any
additional information from the caller.

This is not going to fly for two reasons. First, in general, the problem
of figuring out from source code how many arguments the function is
going to access is equivalent to the halting problem. Which, as we all
know, is undecidable. Even for a moderately complicated code, I don't
think modern compilers are that smart. And you probably won't find too
many programmers willing to hand-craft assembly code.

A more realistic approach would be perhaps to instrument va_start,
va_arg and va_end. The implementation could work some housekeeping into
them to keep track of how deep up the stack they reached. Supporting
things like vprintf would be tricky, but probably solvable.

None of this adresses the second problem though: a variadic function
doesn't have to use, or even known about, all the parameters it is
given. We have already discussed the fact that printf may be
legitimately called with more parameters than there are format
specifiers in its format string. Consider this example:

int find(int what, ...) {
va_list vl;
va_start(vl, what);
int ret;
for (ret = 0; what != va_arg(vl, int); ++ret) ;
va_end(vl);
return ret;
}

The function looks for a given value in a variadic sequence. The caller
is responsible for terminating with a searched-for value, but the value
may also appear in the middle. Once the function finds the value,
there's no way to know whether or not this is the last argument. It
can't correctly pop the stack, unless it's somehow passed additional
information.

Igor Tandetnik

unread,
Jul 19, 2008, 12:24:21 AM7/19/08
to
"Alexander Grigoriev" <al...@earthlink.net> wrote in message
news:Og9zWmU6...@TK2MSFTNGP05.phx.gbl

I've just experimented with this:

void /*__stdcall*/ f(string s);
f("123");

With both cdecl and stdcall, the calling code is running string
constructor, and the code inside f() is calling ~string destructor. So
it doesn't seem to matter who pops the stack.

Alf P. Steinbach

unread,
Jul 19, 2008, 12:43:41 AM7/19/08
to
* Igor Tandetnik:
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:s76dnfnYGKVeUR3V...@posted.comnet
>> * Igor Tandetnik:
>>> "Alf P. Steinbach" <al...@start.no> wrote in message
>>> news:XMudnXxazN5UzR3V...@posted.comnet
>>>> * Igor Tandetnik:
>>>>> With stdcall, the function is responsible for removing its
>>>>> parameters from the stack. To do this, it must know how many
>>>>> parameters there are, and thus cannot take variable number of
>>>>> parameters.
>>>> I'm sorry, that's incorrect (counter-example below).
>>> Your example uses additional domain-specific information that the
>>> compiler doesn't have, in general: that the first parameter specifies
>>> the total number of remaining parameters,
>> Are you of the impression that a vararg routine can access its
>> arguments without some information about them?
>
> I think I finally understand what you are trying to say. Please correct
> me if I'm wrong.

OK.


> It goes like this.
>
> A variadic function needs some way to know which parameters it is given.
> It could be a printf-like format string, a counter passed as the first
> parameter, or perhaps some sentinel value terminating the sequence.

OK.


> If
> the compiler can somehow figure out how the function accesses its
> parameters (presumably by semantic analysis of its source code), it can
> figure out how many there are, and pop the stack correctly without any
> additional information from the caller.

Far-fetched.


> This is not going to fly for two reasons. First, in general, the problem
> of figuring out from source code how many arguments the function is
> going to access is equivalent to the halting problem. Which, as we all
> know, is undecidable. Even for a moderately complicated code, I don't
> think modern compilers are that smart. And you probably won't find too
> many programmers willing to hand-craft assembly code.

OK.


> A more realistic approach would be perhaps to instrument va_start,
> va_arg and va_end. The implementation could work some housekeeping into
> them to keep track of how deep up the stack they reached. Supporting
> things like vprintf would be tricky, but probably solvable.

Or a complete redesign of that library support.


> None of this adresses the second problem though: a variadic function
> doesn't have to use, or even known about, all the parameters it is
> given. We have already discussed the fact that printf may be
> legitimately called with more parameters than there are format
> specifiers in its format string. Consider this example:
>
> int find(int what, ...) {
> va_list vl;
> va_start(vl, what);
> int ret;
> for (ret = 0; what != va_arg(vl, int); ++ret) ;
> va_end(vl);
> return ret;
> }
>
> The function looks for a given value in a variadic sequence. The caller
> is responsible for terminating with a searched-for value, but the value
> may also appear in the middle. Once the function finds the value,
> there's no way to know whether or not this is the last argument. It
> can't correctly pop the stack, unless it's somehow passed additional
> information.

Right.

Essentially then upon seeing

int __stdcall find( int what, ... );

all the compiler needs to remember is to, in its own way, pass the number of
argument bytes, and remember to not adjust stack pointer at call site. This is
similar to how it must take some action for a __stdcall member function (COM),
or for an RVO optimization. That makes the compiled form compiler-specific.

It could be elaborated on with additional notation like

__declspec( nfrs_rgssz ) int __stdcall find( int what, ... );

(darned if I can remember ordinary syntax for __declspec but something like
that), and here the compiler would know that it doesn't need to pass the number
of argument bytes because the function infers that from the arguments.

If one wishes such functions to be exportable from DLLs in language-independent
manner, then a global convention would have to be pushed by e.g. MS and made
part of __stdcall. I see no need for that (the only good thing would slightly
improved possibility of dynamic error checking for "..." functions, but "..."
functions are to be avoided anyway, so, would mostly be a net cost). But the
point is that there's nothing in the __stdcall convention that prevents it.


Cheers,

Alexander Grigoriev

unread,
Jul 19, 2008, 10:04:41 AM7/19/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:%23rvqUdV...@TK2MSFTNGP06.phx.gbl...

>> Here comes another thing of callng convention: who is destroying
>> non-reference non-POD arguments of a function. Such objects are
>> destroyed upon function return (according to ISO14882 1.9/13, a
>> function call is full expression). These should be destroyed before
>> the stack pointer is adjusted back.
>
> I've just experimented with this:
>
> void /*__stdcall*/ f(string s);
> f("123");
>
> With both cdecl and stdcall, the calling code is running string
> constructor, and the code inside f() is calling ~string destructor. So it
> doesn't seem to matter who pops the stack.
> --

Yes, it does make sense to destroy those inside the function. That saves
code size.


Liviu

unread,
Jul 19, 2008, 12:28:45 PM7/19/08
to
"Alexander Grigoriev" <al...@earthlink.net> wrote in message
> news:Otgrjha6...@TK2MSFTNGP03.phx.gbl...

>
> "Igor Tandetnik" <itand...@mvps.org> wrote in message
> news:%23rvqUdV...@TK2MSFTNGP06.phx.gbl...
>>> Here comes another thing of callng convention: who is destroying
>>> non-reference non-POD arguments of a function. Such objects are
>>> destroyed upon function return (according to ISO14882 1.9/13, a
>>> function call is full expression). These should be destroyed before
>>> the stack pointer is adjusted back.
>>
>> I've just experimented with this:
>>
>> void /*__stdcall*/ f(string s);
>> f("123");
>>
>> With both cdecl and stdcall, the calling code is running string
>> constructor, and the code inside f() is calling ~string destructor.
>> So it doesn't seem to matter who pops the stack.
>
> Yes, it does make sense to destroy those inside the function. That
> saves code size.

Also makes sense in terms of exception handling, so that he who
compiles f() doesn't have to rely on caller's mercy (and /E options)
to dispose of temporaries should an exception actually be thrown.

Alf P. Steinbach

unread,
Jul 19, 2008, 1:50:02 PM7/19/08
to
* Alex Blekhman:
> "Alf P. Steinbach" wrote:
>> As you can see at the assembly language level [...] there's no
>> problem. [...] so it's just a tool problem, and for that matter,
>> also library problem, that there's no support.
>
> I think there is a problem at the assembly language level.
> Actually, it is the __stdcall calling convention itself is a
> problem.

Well, no. Only from the perspective of what we're used to. You'd find decimal
number system problematic if you came from an octal culture, being used to that.


> You have to pass a number of actual parameters to the collee.
> Otherwise there is no way to clean the stack from within a
> function. This is inherent limitation of __stdcall calling
> convention no matter what tool or library you may use. __cdecl
> delegates this problem to the caller, so there is nothing to wory
> about for the function itself.

Similarly, with stdcall there's nothing to worry about for the calling code. And
there can be an arbitrary number of callers of each function. So (note, I'm
aware that this argument is fallacious, but so is the above! :-)) stdcall is
actually much less problematic than cdecl, to the tune of avrg nr of callers.

Alf P. Steinbach

unread,
Jul 19, 2008, 2:01:35 PM7/19/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:sfKdnW8tnNsu2xzV...@posted.comnet...
>> * Liviu:
>>> Please note that once you impose additional requirements on the
>>> caller
>>> code (pass number of params, set some sort of stack frame, etc) then
>>> that's no longer the __stdcall convention.
>> Sorry, I didn't see that. That's too stupid for my limited English
>> vocabulary. Do you think we're engaged in some children's "guess my
>> riddle"?
>>
>> Anyways, we were discussing a statement that stdcall convention *could
>> not* support varargs: "thus, ...".
>
> Let's just rephrase that to "__stdcall can technically call any random
> memory address, including that of a variadic function, except it's not
> safe to do so and will crash more often than not" ;-)

Like, if two people disagree about the temperature on the surface of the Sun,
one person correctly saying it's about 6000 degrees C, the other that it's tens
of millions of degrees, a compromise of about 100 000 degrees would be correct.

Well, of course it isn't: for technical issues and physical issues and math
issues and so on, compromise answers aren't good at all. They're at most good
for getting people to get along. And nothing much else, can even have negative
effects, especially if you send people into an area based on compromise idiocy.

So, your rephrasing compromise above is simply not correct. If you omit a
required argument when you call a routine, then you have Undefined Behavior
whether that routine is cdecl or stdcall. Conversely, if you do everything
Right, then that call is OK whether the routine is cdecl or stdcall.

This talk about "not safe" and "will crash [sometimes]", it's just attempted FUD
(spreading Fear, Uncertainty and Doubt); technically *incorrect*, appealing to
emotions.

From my point of view that attempt at FUD is pure idiocy.


>> Then it's completely false and misleading to snub any counter-example
>> by "that's not stdcall as I know it".
>
> You forgot a comma after stdcall ;-)

Nope.


>> That argument is "I define stdcall as a convention that can't support
>> vargarg, therefore, it can't". Do you see how stupid that is? Well,
>> probably not. But it is, it's below a snail's level of intelligence.
>
> It's been a long day, and the OP's question was fully answered long ago.
> No reason to waste your time, indeed.

I don't think George's question has been fully answered as long as e.g. you are
still posting technically incorrect statements in this thread.

George, assuming he's human ;-), must be wondering whether e.g. your postings
could contain some grain of truth, that perhaps there's something there.

But there isn't.

Igor Tandetnik

unread,
Jul 19, 2008, 2:21:57 PM7/19/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:8oGdnZWsnfie7BzV...@posted.comnet
> * Igor Tandetnik:

>> The function looks for a given value in a variadic sequence. The
>> caller is responsible for terminating with a searched-for value, but
>> the value may also appear in the middle. Once the function finds the
>> value, there's no way to know whether or not this is the last
>> argument. It can't correctly pop the stack, unless it's somehow
>> passed additional information.
>
> Right.
>
> Essentially then upon seeing
>
> int __stdcall find( int what, ... );
>
> all the compiler needs to remember is to, in its own way, pass the
> number of argument bytes

You seem to be proving my point. The only reason stdcall was invented in
the first place, is because it shaves one machine instruction off the
call site (the "add esp, <size of arguments>" one) compared to cdecl.
This is its one and only advantage over cdecl. You are putting a
different machine instruction right back into the call site, and calling
it a new and improved stdcall.

It seems to be an argument over terminology. You seem to say: stdcall is
any arrangement, no matter how complicated, where the callee, rather
than the caller, ends up cleaning the stack. I concede: under this
expanded definition of stdcall, it is possible to have a stdcall
variadic function.

However, as a developer, I don't care who cleans the stack. All I care
about is that my programs be smaller and/or faster. The "traditional"
stdcall calling convention (one currently supported by VC compiler)
makes fixed-signature calls smaller compared with cdecl, at the expense
of not supporting variadic functions. Your proposed expanded stdcall
would handle variadic functions, but in a way that makes a program
larger than an equivalent program using cdecl for variadic functions.

Thus, there's a good reason for tools to support "traditional" stdcall,
and so they do. There's no good reason for tools to support "expanded"
stdcall, and so, unsurprisingly, they don't. I for one am happy to
continue marking my variadic functions __cdecl.

> This is similar to how it must take some action for a
> __stdcall member function (COM)

I don't understand this statement. What additional action does the
compiler perform for a __stdcall member function, that it wouldn't for,
say, a __cdecl member function?

> or for an RVO optimization.

I'm not sure what RVO has to do with anything. As far as I can tell, RVO
doesn't affect caller at all, it's purely the callee's implementation
detail. No special provision is needed in the calling convention to
enable RVO.

Consider:

string f() { return "123"; }

When a function returns a class, the compiler effectively rewrites it as

void f(void* p);

The caller passes a pointer to uninitialized buffer sufficiently large
to store the instance of the class. The function then can be implemented
in two ways:

// without RVO
void f(void* p) {
// create a temporary on the stack
string temp = "123";
// copy-construct into caller-provided buffer
new(p) string(temp);
}

// with RVO
void f(void* p) {
// construct directly into caller-provided buffer
new(p) string("123");
}

The caller sees no difference.

> It could be elaborated on with additional notation like
>
> __declspec( nfrs_rgssz ) int __stdcall find( int what, ... );
>
> (darned if I can remember ordinary syntax for __declspec but
> something like that), and here the compiler would know that it
> doesn't need to pass the number of argument bytes because the
> function infers that from the arguments.

But how would the function infer that? The completely automatic way is
"far fetched", as you noted. Presumably there would have to be some
compiler directive whereby the programmer would manually tell the
compiler how many arguments he thinks the function was passed. And the
caller would have to be careful not to pass any extraneous arguments.
All in all, a lot of manual work just to save one machine instruction at
call site, and any errors are fatal and undiagnosable at compile time. I
doubt you'll see much enthusiasm for this idea.

Alf P. Steinbach

unread,
Jul 19, 2008, 2:42:44 PM7/19/08
to
* Igor Tandetnik:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:8oGdnZWsnfie7BzV...@posted.comnet
>> * Igor Tandetnik:
>>> The function looks for a given value in a variadic sequence. The
>>> caller is responsible for terminating with a searched-for value, but
>>> the value may also appear in the middle. Once the function finds the
>>> value, there's no way to know whether or not this is the last
>>> argument. It can't correctly pop the stack, unless it's somehow
>>> passed additional information.
>> Right.
>>
>> Essentially then upon seeing
>>
>> int __stdcall find( int what, ... );
>>
>> all the compiler needs to remember is to, in its own way, pass the
>> number of argument bytes
>
> You seem to be proving my point. The only reason stdcall was invented in
> the first place, is because it shaves one machine instruction off the
> call site (the "add esp, <size of arguments>" one) compared to cdecl.

I don't think it can be true that the designers reasoned that way, but it's
possible -- and perhaps they've told you their reasoning?


> This is its one and only advantage over cdecl.

No, that is incorrect.


> You are putting a
> different machine instruction right back into the call site,

In some cases, if needed, yes.


> and calling
> it a new and improved stdcall.

No, both of those are lies. I.e., 2 lies in that short little snippet of text.
But of course you may be thinking that those are not lies but accurate
descriptions of reality, to the extent that your memory permits you to perceive
reality and not a distorted version of it. In that case, please try to provide
textual quotes where I (1) call that a "new stdcall" (or imply that, or
similar), and (2) call that an "improved stdcall" (or imply that, or similar).


> It seems to be an argument over terminology. You seem to say: stdcall is
> any arrangement, no matter how complicated, where the callee, rather
> than the caller, ends up cleaning the stack. I concede: under this
> expanded definition of stdcall, it is possible to have a stdcall
> variadic function.

How can you say that Microsoft's own definition of stdcall is an expanded one?

What is the /unexpanded/ (in your view) definition of stdcall?

Please provide a link to such unexpanded definition.

[snip]


>
>> It could be elaborated on with additional notation like
>>
>> __declspec( nfrs_rgssz ) int __stdcall find( int what, ... );
>>
>> (darned if I can remember ordinary syntax for __declspec but
>> something like that), and here the compiler would know that it
>> doesn't need to pass the number of argument bytes because the
>> function infers that from the arguments.
>
> But how would the function infer that? The completely automatic way is
> "far fetched", as you noted.

Yes, and the manual way is not.

It is not at all difficult to /design/ foo so that e.g. first argument says how
many arguments follow. Nor is it difficult to provide foo with a library routine
it can call in order to get correct automatic stack cleanup prior to returning.

It would not be as safe as automated passing of nr. of bytes, though. Using a
directive such as above would be back down at the unsafety level of cdecl, just
in order to shave off one caller instruction.


> Presumably there would have to be some
> compiler directive whereby the programmer would manually tell the
> compiler how many arguments he thinks the function was passed.

Huh. At this point I think you're into the old habit of speculating about
impractical, undoable schemes again. Like the can't doer's "I can /easily/
envision one way to attempt to do that that could not work, so this can't work",
so I just snip the rest.

Liviu

unread,
Jul 19, 2008, 2:42:44 PM7/19/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote in message
>>>
>>> Anyways, we were discussing a statement that stdcall convention
>>> *could not* support varargs: "thus, ...".
>>
>> Let's just rephrase that to "__stdcall can technically call any
>> random memory address, including that of a variadic function,
>> except it's not safe to do so and will crash more often than not" ;-)
>
> So, your rephrasing compromise above is simply not correct.

Oh, there we go again... What's your example if not a __stdcall into the
address of a variadic function. And what's knurre(1, 101, 102, 103) if
not a crash.

What part is "simply not correct" again?

> If you omit a required argument when you call a routine, then you have
> Undefined Behavior whether that routine is cdecl or stdcall.

If you omit a required argument that's a well defined "compiler error"
behavior.

If you refer to the "..." part of a variadic function, then "required"
can only be interpreted relative to the particular function you are
calling. Printf, for example, specifies undefined behavior in such a
case. An empty function, on the other hand, wouldn't care and could
legitimately specify that in its docs.

> Conversely, if you do everything Right, then that call is OK whether
> the routine is cdecl or stdcall.

If you pass excess arguments to printf, it is required to ignore them.

> I don't think George's question has been fully answered as long as
> e.g. you are still posting technically incorrect statements

Talk is cheap. Instead, why don't you "please fill in this part"...

| ; this is the caller code ........................................
|
| ; calling a variadic _stdcall function with 1 argument
| push eax
| call _printf
|
| ; calling the same variadic _stdcall function with 2 arguments
| push eax
| push eax
| call _printf
|
| ; ...etc
|
| ; please fill in this part ........................................
|
| _printf:
| ; --- your code here ---
| ; --- doesn't have to do anything except _not_ corrupt the stack ---

If you know you can't, maybe you should then tone down your rhetoric
and admit that all you have is one example of a carefully handcrafted
variadic function which pops its own stack, using a fragile technique
arguably worse than both __stdcall and __cdecl, and which "by design"
could not implement even a compliant printf. That much, no one disputed.

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 19, 2008, 3:09:19 PM7/19/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote
>> * Liviu:
>>> "Alf P. Steinbach" <al...@start.no> wrote in message
>>>> Anyways, we were discussing a statement that stdcall convention
>>>> *could not* support varargs: "thus, ...".
>>> Let's just rephrase that to "__stdcall can technically call any
>>> random memory address, including that of a variadic function,
>>> except it's not safe to do so and will crash more often than not" ;-)
>> So, your rephrasing compromise above is simply not correct.
>
> Oh, there we go again... What's your example if not a __stdcall into the
> address of a variadic function.

I surmise that you may have had something in mind when writing that question,
but I'm not able to guess what it could have been.


> And what's knurre(1, 101, 102, 103) if
> not a crash.

It's a precondition (function contract) violation.

It's the same as writing printf( "%d" ).

You have lied to the function, and should not be suprised that it doesn't then
provide its otherwise guaranteed result.


> What part is "simply not correct" again?

Your FUD about unsafety might-crash and so on, is not correct: it's just FUD.


>> If you omit a required argument when you call a routine, then you have
>> Undefined Behavior whether that routine is cdecl or stdcall.
>
> If you omit a required argument that's a well defined "compiler error"
> behavior.

Assuming you meant "compilation error", not "compiler error" (the latter would
literally yield a correct statement, but I simply don't believe you're up that
level of intelligence):

Here you are confusing programmer and compiler.

Yes, if you the programmer omit a required argument and the programming language
specifies sufficient static type checking and the compiler implements that
checking, then you get a compilation error.

No, if "you" are the compiler and are emitting machine code you do not get a
compiler error if you forget one argument.

> If you refer to the "..." part of a variadic function, then "required"
> can only be interpreted relative to the particular function you are
> calling. Printf, for example, specifies undefined behavior in such a
> case. An empty function, on the other hand, wouldn't care and could
> legitimately specify that in its docs.

I can't make heads or tails of the above, probably because it's completely
meaningless.


>> Conversely, if you do everything Right, then that call is OK whether
>> the routine is cdecl or stdcall.
>
> If you pass excess arguments to printf, it is required to ignore them.

I'm sorry, that's not a meaningful statement to me.


>> I don't think George's question has been fully answered as long as
>> e.g. you are still posting technically incorrect statements
>
> Talk is cheap. Instead, why don't you "please fill in this part"...

I'm sorry again, the described challenge (below) is completely meaningless.


> | ; this is the caller code ........................................
> |
> | ; calling a variadic _stdcall function with 1 argument
> | push eax
> | call _printf
> |
> | ; calling the same variadic _stdcall function with 2 arguments
> | push eax
> | push eax
> | call _printf
> |
> | ; ...etc
> |
> | ; please fill in this part ........................................
> |
> | _printf:
> | ; --- your code here ---
> | ; --- doesn't have to do anything except _not_ corrupt the stack ---
>
> If you know you can't, maybe you should then tone down your rhetoric

I'm sorry yet again, that's completely meaningless to me. Like, if you know you
can't travel to the moon (Luna), perhaps you should tone down your rhetoric.
It's lunacy.


Cheers, & hth.,

Liviu

unread,
Jul 19, 2008, 3:22:46 PM7/19/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>>
>> And what's knurre(1, 101, 102, 103) if not a crash.
>
> It's a precondition (function contract) violation.
>
> It's the same as writing printf( "%d" ).

No, if anything, it's the same as writing printf("%d", 1, 2, 3). Which,
incidentally, not only works as expected, but is actually required by
the standard to do so.

> I'm sorry, that's not a meaningful statement to me.
>

> I'm sorry again, the described challenge (below) is completely
> meaningless.
>

> I'm sorry yet again, that's completely meaningless to me.

;-)

Bye now,
Liviu


Alf P. Steinbach

unread,
Jul 19, 2008, 3:48:50 PM7/19/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote
>> * Liviu:
>>> And what's knurre(1, 101, 102, 103) if not a crash.
>> It's a precondition (function contract) violation.
>>
>> It's the same as writing printf( "%d" ).
>
> No, if anything, it's the same as writing printf("%d", 1, 2, 3).

No, it isn't. In C99 that printf call is well-defined: you're not lying to the
function. In the case of the call to knurre, you're lying to the function.

The relevant feature is: lying to the function or not.


> Which,
> incidentally, not only works as expected, but is actually required by
> the standard to do so.

Which means you did not lie to the function in this call.

But you did in the other.

The relevant feature is: lying to the function or not.


>> I'm sorry, that's not a meaningful statement to me.
>>
>> I'm sorry again, the described challenge (below) is completely
>> meaningless.
>>
>> I'm sorry yet again, that's completely meaningless to me.
>
> ;-)

Well, you might think it's fun to post a lot of gibberish and idiocy, but you're
wasting mine and everyone else's time, and you're possibly confusing readers who
might think you have a point or two regarding the issue we're discussing.

Liviu

unread,
Jul 19, 2008, 4:33:42 PM7/19/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:P4KdnTkCvom42B_V...@posted.comnet...

>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Liviu:
>>>> And what's knurre(1, 101, 102, 103) if not a crash.
>>> It's a precondition (function contract) violation.
>>>
>>> It's the same as writing printf( "%d" ).
>>
>> No, if anything, it's the same as writing printf("%d", 1, 2, 3).
>
> No, it isn't. In C99 that printf call is well-defined: you're not
> lying to the function. In the case of the call to knurre, you're lying
> to the function.

Then call it for what it is "the __knurre calling convention, which
works for the knurre variadic function", and stop claiming any more
general significance to your example. Btw, that's what I wrote before:

| If you know you can't, maybe you should then tone down your rhetoric

| and admit that all you have is one example of a carefully handcrafted
| variadic function which pops its own stack, using a fragile technique
| arguably worse than both __stdcall and __cdecl, and which "by design"
| could not implement even a compliant printf.

to which you replied

>> I'm sorry yet again, that's completely meaningless to me.

Glad to see that you've answered your own confusion ;-)

> Well, you might think it's fun to post a lot of gibberish and idiocy,

Hmm..

> Or perhaps you're just weaseling. But I think you're simply stupid


>
> two just about equally thickheaded persons
>

> Do you see how stupid that is?
> Well, probably not. But it is, it's below a snail's level of
> intelligence.
>

> simply don't believe you're up that level of intelligence
>

> perhaps you should tone down your rhetoric. It's lunacy.
>

> That's too stupid for my limited English vocabulary.

I'd say your English is fine. Put your worries elsewhere ;-)

Done and out,
Liviu


Alex Blekhman

unread,
Jul 19, 2008, 5:03:14 PM7/19/08
to
"Alf P. Steinbach" wrote:
>> I think there is a problem at the assembly language level.
>> Actually, it is the __stdcall calling convention itself is a
>> problem.
>
> Well, no. Only from the perspective of what we're used to.
> You'd find decimal number system problematic if you came from an
> octal culture, being used to that.

I think that this whole thread does exist only due to different
understanding of a terminology. Clearly, all participants are
perfectly aware of the ways how compiler passes parameters to a
function and how calling stack is popped up.

The only point that raises an argument is how to call it. I
couldn't find a formal definition of what the calling convention
is. It is, well, a mere convention, which is accepted by major
compiler vendors. So there is a certain flexibility when one could
draw a line between a calling convention and documented function
requirements.

You claim that a special additional argument (which may be
arragnged either manually or automatically by the compiler) still
leaves a function within the boundaries of the same calling
convention, namely __stdcall. That the special meaning of the
argument is up to the function and has nothing to do with a
calling convention.

In my opinion, such interpretation of a calling convention is too
broad and vague. All the more so, if we intend to require an
automatic support by the compiler. I think that once a calling
mechanics is altered, then the convention is breached. By calling
mechanics I mean a number of registers involved and their roles
during a call and obligations that caller and callee have to
fulfill.

That's why, for instance, there is additional calling convention
__thiscall, which is not different from __stdcall, apart of the
special role of ECX register. Authors of the compiler chose to
give a new name to this small alteration of __stdcall calling
convention since there is a new factor that tools must take into
account.

According to my understanding of what a calling convention
consists of, your example is beyond the limits of __stdcall
capabilities because it requires new convention about the meaning
of parameters.

Alex

Alf P. Steinbach

unread,
Jul 19, 2008, 5:14:12 PM7/19/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:P4KdnTkCvom42B_V...@posted.comnet...
>> * Liviu:
>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>> * Liviu:
>>>>> And what's knurre(1, 101, 102, 103) if not a crash.
>>>> It's a precondition (function contract) violation.
>>>>
>>>> It's the same as writing printf( "%d" ).
>>> No, if anything, it's the same as writing printf("%d", 1, 2, 3).
>> No, it isn't. In C99 that printf call is well-defined: you're not
>> lying to the function. In the case of the call to knurre, you're lying
>> to the function.
>
> Then call it for what it is "the __knurre calling convention, which
> works for the knurre variadic function",

That function is declared as __stdcall. So it's stdcall calling convention.


> and stop claiming any more
> general significance to your example.

I'm sorry, but that seems designed to deceive a reader.

And if so, then it's purely a lie (a statement intended to cause others to
believe something that you know is untrue).

If you had something concrete in mind, why didn't you write that?


> Btw, that's what I wrote before:
>
> | If you know you can't, maybe you should then tone down your rhetoric
> | and admit that all you have is one example of a carefully handcrafted
> | variadic function which pops its own stack, using a fragile technique

Didn't comment on this before, but you're as wrong here as you can be, which of
course is not surprising, but still: the technique is not fragile, on the
contrary it provides (when implemented by a compiler) much more safety than
cdecl variable argument passing, because it provides the function a means to
check that it has at least the number of arguments indicated in other ways.

Without tool support it is more fragile than a technique with tool support.

I think it requires a very high degree of stupidity to not see that.


> | arguably worse than both __stdcall and __cdecl,

Again, as wrong as you can be, or if possible even wronger. :-) That technique,
using a __stdcall function, can't be worse than __stdcall. Pardon me for
pointing out the inconsistency and further, that it's what I expect from you.


> and which "by design"
> could not implement even a compliant printf.

Again, that's as wrong as you can be, and this time you have even been answered
in detail before: it's trivial to implement a compliant __stdcall printf using
that technique, if you have tool support (the compiler passes the number of
bytes). If you don't have tool support it is of course difficult. And that's how
it is with anything you don't have tool support for.


> to which you replied
>
>>> I'm sorry yet again, that's completely meaningless to me.
>
> Glad to see that you've answered your own confusion ;-)

You provide a meaningless challenge to square the circle, and (disregarding that
squaring the circle has actually been solved) you think anyone would take up
that challenge -- and you think they're confused when they describe it as
meaningless?

Pardon me, but you do sound like an idiot, hence I've repeated explanations of
various things above.

Alf P. Steinbach

unread,
Jul 19, 2008, 5:26:46 PM7/19/08
to
* Alex Blekhman:

>> You'd find decimal number system problematic if you came from an
>> octal culture, being used to that.
>
> According to my understanding of what a calling convention
> consists of, your example is beyond the limits of __stdcall
> capabilities because it requires new convention about the meaning
> of parameters.

May be. But the point is that stdcall could easily have used this technique.
Hence it's incorrect to say that stdcall cannot or could not support variadic
number of arguments (as Igor did) -- a single counter-example is enough to
establish that, and such counter-example has been provided.

Now whether to call what-stdcall-could-have-been stdcall or not, that's a
separate issue, one of practicality; I think that for a language such as C++,
where the function has variadic number of arguments if and only if it has "..."
formal argument, there's no need for a separate name: the "..." is enough.

But for a language like C -- unless it's become somewhat more strictly typed
in recent years -- a separate name might be a good idea.

So it's relative, as most things are. ;-)

I just wish the other two participants in this thread would stop posting inane,
idiotic, meaningless articles, engaging my calling-names circuits all the time
(or perhaps that's why they do it, if so then it's not very nice of them).

Liviu

unread,
Jul 19, 2008, 9:51:09 PM7/19/08
to

"Alf P. Steinbach" <al...@start.no> wrote in message
news:25WdnXEUbI2EwR_V...@posted.comnet...

>* Alex Blekhman:
>>> You'd find decimal number system problematic if you came from an
>>> octal culture, being used to that.
>>
>> According to my understanding of what a calling convention consists
>> of, your example is beyond the limits of __stdcall capabilities
>> because it requires new convention about the meaning of parameters.
>
> May be. But the point is that stdcall could easily have used this
> technique.

No. The point is that it doesn't, and it couldn't - short of redefining
what __stdcall means.

> Hence it's incorrect to say that stdcall cannot or could not support
> variadic number of arguments (as Igor did) -- a single
> counter-example is enough to establish that, and such counter-example
> has been provided.

Huh? Assume the assertion "__stdcall and __cdecl are different calling
conventions". Would you really argue that the assertion is false because
a function, say "void knurre2();" can be called under either convention?

Cheers,
Liviu


Liviu

unread,
Jul 19, 2008, 9:49:46 PM7/19/08
to

"Alf P. Steinbach" <al...@start.no> wrote in message
news:LOmdnQIqtJm6xB_V...@posted.comnet...

>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote in message
>> news:P4KdnTkCvom42B_V...@posted.comnet...
>>> * Liviu:
>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>> * Liviu:
>>>>>> And what's knurre(1, 101, 102, 103) if not a crash.
>>>>> It's a precondition (function contract) violation.
>>>>>
>>>>> It's the same as writing printf( "%d" ).
>>>> No, if anything, it's the same as writing printf("%d", 1, 2, 3).
>>> No, it isn't. In C99 that printf call is well-defined: you're not
>>> lying to the function. In the case of the call to knurre, you're
>>> lying to the function.
>>
>> Then call it for what it is "the __knurre calling convention, which
>> works for the knurre variadic function",
>
> That function is declared as __stdcall. So it's stdcall calling
> convention.

You must mean those 4 distinct functions declarations? No. What's
declared there as __stdcall is just a hack on the caller side to fool
the compiler into issuing bogus calls to what's eventually resolved as
the entry point address of your knurre function.

> the technique is not fragile, on the contrary it provides (when
> implemented by a compiler)

> ...


> Without tool support it is more fragile than a technique with tool
> support.

> ...


> it's trivial to implement a compliant __stdcall printf using that

> technique if you have tool support (the compiler passes the number of


> bytes). If you don't have tool support it is of course difficult.

Do you really not see all the when's and if's and could's in what you
wrote above, or do you just deliberately choose to ignore the obvious?

In case you lost sight of where this started, discussion was about
__stdcall, which has been established last millennium. Whatever you
digress now about possible future calling conventions, however clever or
exciting (not saying that's the case), but has no relevance whatsoever
to the topics of existing __stdcall.

Cheers,
Liviu

Alf P. Steinbach

unread,
Jul 19, 2008, 11:22:07 PM7/19/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:LOmdnQIqtJm6xB_V...@posted.comnet...
>> * Liviu:
>>> "Alf P. Steinbach" <al...@start.no> wrote in message
>>> news:P4KdnTkCvom42B_V...@posted.comnet...
>>>> * Liviu:
>>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>>> * Liviu:
>>>>>>> And what's knurre(1, 101, 102, 103) if not a crash.
>>>>>> It's a precondition (function contract) violation.
>>>>>>
>>>>>> It's the same as writing printf( "%d" ).
>>>>> No, if anything, it's the same as writing printf("%d", 1, 2, 3).
>>>> No, it isn't. In C99 that printf call is well-defined: you're not
>>>> lying to the function. In the case of the call to knurre, you're
>>>> lying to the function.
>>> Then call it for what it is "the __knurre calling convention, which
>>> works for the knurre variadic function",
>> That function is declared as __stdcall. So it's stdcall calling
>> convention.
>
> You must mean those 4 distinct functions declarations? No. What's
> declared there as __stdcall is just a hack on the caller side to fool
> the compiler into issuing bogus calls to what's eventually resolved as
> the entry point address of your knurre function.

Don't know how you manage to write so much wrong.

Perhaps you're trying to goad me even further than you've managed so far.

Whatever.

The calls are not bogus: they're real, and they work. The compiler is not
fooled, it's told the truth, and that's why the calls work. The calls are not
"eventually" resolved, they're directly resolved, there's no indirection.

So.


>> the technique is not fragile, on the contrary it provides (when
>> implemented by a compiler)
>> ...
>> Without tool support it is more fragile than a technique with tool
>> support.
>> ...
>> it's trivial to implement a compliant __stdcall printf using that
>> technique if you have tool support (the compiler passes the number of
>> bytes). If you don't have tool support it is of course difficult.
>
> Do you really not see all the when's and if's and could's in what you
> wrote above,

I /wrote/ the 'if's, and there are no 'could's.


> or do you just deliberately choose to ignore the obvious?

Apparently you think something is obvious. Given the quality of your reasoning
(so far non-existent) it's impossible to guess. What is it?


> In case you lost sight of where this started, discussion was about
> __stdcall, which has been established last millennium.

I think that's correct. Checking title of this thread... "calling convention
stdcall and cdecl call", yes, it seems you have grasped at least roughly what
it's about.


> Whatever you digress now about possible future calling conventions,

You're trying to lie again. Don't. Just, every time you get the urge to lie, the
urge to deceive, then smoke a cigarette, take five push-ups, whatever.

For the record, I'm not digressing and have not been digressing.

All I've done has been to respond to your (less than grokkable) responses to me.


> however clever or
> exciting (not saying that's the case), but has no relevance whatsoever
> to the topics of existing __stdcall.

Say that Igor, who started this sub-thread about what stdcall can and can't (or,
if you have trouble parsing that, read "could" or "couldn't").

I'm on record in this thread as agreeing with what you write here.

But now, simply by putting that forth as your own idea, which I don't doubt it
is!, you're making me suspect that I might have reached the wrong conclusion --
because the probability that you should write something meaningful is ~0.


Cheers,

Alf P. Steinbach

unread,
Jul 19, 2008, 11:31:44 PM7/19/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:25WdnXEUbI2EwR_V...@posted.comnet...
>> * Alex Blekhman:
>>>> You'd find decimal number system problematic if you came from an
>>>> octal culture, being used to that.
>>> According to my understanding of what a calling convention consists
>>> of, your example is beyond the limits of __stdcall capabilities
>>> because it requires new convention about the meaning of parameters.
>> May be. But the point is that stdcall could easily have used this
>> technique.
>
> No. The point is that it doesn't, and it couldn't - short of redefining
> what __stdcall means.

Heh, you're trying to goad me again, by writing (unmentionable).

OK, you've succeeded.

But I'll just say that your statement above is (1) incorrect, (2) mostly
meaningless, and (3) the kind of statement that you could produce.


>> Hence it's incorrect to say that stdcall cannot or could not support
>> variadic number of arguments (as Igor did) -- a single
>> counter-example is enough to establish that, and such counter-example
>> has been provided.
>
> Huh? Assume the assertion "__stdcall and __cdecl are different calling
> conventions". Would you really argue that the assertion is false because
> a function, say "void knurre2();" can be called under either convention?

stdcall and cdecl are different calling conventions.

A function with no arguments and exported from a DLL can be called as either
convention, by fooling around a little with this and that.

The assertion that cdecl and stdcall are different isn't false, but an assertion
that "thus, no function can be called as both stdcall and cdecl" would be false.

The assertion that "thus, stdcall can't support variable number of arguments" is
false in that limited sense of a special case exception existing, AND

it's also false in the more general sense that stdcall can support the complete,
general notion of variable number of arguments, namely C++ "...", that cdecl
supports, and this more general case has been demonstrated (even if you don't
grok that it has, just trust me, it has) -- and if MS is keen enough on
providing security measures that they pretend that perfectly fine standard
library functions have been deprecated, perhaps they'll do this also for
safety's sake, even though it's like closing barn door after horse has left.

Liviu

unread,
Jul 20, 2008, 3:04:46 AM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:

>>
>> Huh? Assume the assertion "__stdcall and __cdecl are different
>> calling conventions". Would you really argue that the assertion is
>> false because a function, say "void knurre2();" can be called under
>> either convention?
>
> The assertion that cdecl and stdcall are different isn't false,

Sigh ;-)

> but an assertion that "thus, no function can be called as both stdcall
> and cdecl" would be false.

True, but irrelevant. Which pretty much sums up the rest of your
pointless subthread.

Now please flush your output buffer and consider the stream closed ;-)

Cheers,
Liviu

Alf P. Steinbach

unread,
Jul 20, 2008, 3:35:27 AM7/20/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote
>> * Liviu:
>>> Huh? Assume the assertion "__stdcall and __cdecl are different
>>> calling conventions". Would you really argue that the assertion is
>>> false because a function, say "void knurre2();" can be called under
>>> either convention?
>> The assertion that cdecl and stdcall are different isn't false,
>
> Sigh ;-)

Please pay attention: even if you don't grasp a simple sentence like that at
first, try to study it before concluding that it's forever out of your reach.

You can do it!

You might ask yourself, e.g., how the sentence relates to your question.

Or you might ask me, if you're unable to figure that out yourself (as you
evidently are, since you sigh), so, here it is:

You asked,

"Would you really argue that the assertion [x] is false"

I answered

"The assertion [x] isn't false"

As you can see, if you study this very very hard, it means I *would not* argue,
nor really argue, nor whatever, that assertion [x] is false.

So it *answers your question*.

Why do you then sigh?

Did't you want your question answered?


>> but an assertion that "thus, no function can be called as both stdcall
>> and cdecl" would be false.
>
> True, but irrelevant.

I don't know how better to answer your question, quoted above.

If the answer seems irrelevant, it may be because the question was irrelevant?

Don't you think?[1]


> Which pretty much sums up the rest of your pointless subthread.

Hey, it's your thread. Don't try to blame me. At least it's been fun reading
your excessively inane statements and helpless attempts at trying to pin
silly/idiotic points of view on me. I'm not at all convinced that you're
necessarily more human than George. For it seems humanly impossible to be so
thickheaded (in George's case, instead of manifesting as a belief that he
understands it instead manifests as a tendency to ask again&again in a machine
like manner about what's already been answered umpteen times, i.e. a complete
inability to *understand* what's being said, and merrily multi-posting it all).


> Now please flush your output buffer and consider the stream closed ;-)

Hah. :-)


Cheers,

- Alf

Notes:
[1] Uh, sorry for that question.

Igor Tandetnik

unread,
Jul 20, 2008, 9:36:28 AM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:nNmdnc68-dIuqB_V...@posted.comnet
> * Igor Tandetnik:

>>> Essentially then upon seeing
>>>
>>> int __stdcall find( int what, ... );
>>>
>>> all the compiler needs to remember is to, in its own way, pass the
>>> number of argument bytes
>>
>> You seem to be proving my point. The only reason stdcall was
>> invented in the first place, is because it shaves one machine
>> instruction off the call site (the "add esp, <size of arguments>"
>> one) compared to cdecl.
>
> I don't think it can be true that the designers reasoned that way

Why can't it be true?

> but it's possible -- and perhaps they've told you their reasoning?

They did - there is a written account of it somewhere, but I must admit
I can't find a reference at the moment.

Let's state it this way: I personally claim, looking at the details of
the two conventions, that the only advantage of stdcall over cdecl lies
in removing one machine instruction from the call site.

>> This is its one and only advantage over cdecl.
>
> No, that is incorrect.

In what way is that incorrect?

>> You are putting a
>> different machine instruction right back into the call site,
>
> In some cases, if needed, yes.
>
>
>> and calling
>> it a new and improved stdcall.
>
> No, both of those are lies. I.e., 2 lies in that short little
> snippet of text. But of course you may be thinking that those are
> not lies but accurate descriptions of reality, to the extent that
> your memory permits you to perceive reality and not a distorted
> version of it. In that case, please try to provide textual quotes
> where I (1) call that a "new stdcall" (or imply that, or similar),
> and (2) call that an "improved stdcall" (or imply that, or similar).

By all means. The technique you propose is different from "traditional"
__stdcall as currently implemented by VC compiler: "not supported by
tools". Are you suggesting there exists, or used to exist, a compiler
that implemented stdcall the way you describe? If so, could you cite a
reference? Otherwise, the technique is properly characterized as "new",
as in "never before implemented".

You said your technique would support variadic functions. The
"traditional" stdcall doesn't (again, "not supported by tools"). I
assumed you would claim this fact as an improvement. However, if you
don't maintain your technique is an improvement, then we are in
agreement. It's been my point all along that the mechanism you propose,
while possible, is inferior to existing alternatives.

>> It seems to be an argument over terminology. You seem to say:
>> stdcall is any arrangement, no matter how complicated, where the
>> callee, rather than the caller, ends up cleaning the stack. I
>> concede: under this expanded definition of stdcall, it is possible
>> to have a stdcall variadic function.
>
> How can you say that Microsoft's own definition of stdcall is an
> expanded one?
> What is the /unexpanded/ (in your view) definition of stdcall?

Microsoft's own definition of stdcall doesn't involve passing total size
of arguments to the callee. Your does, at least "in some cases". In this
sense yours is "expanded".

> Please provide a link to such unexpanded definition.

http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx
http://msdn.microsoft.com/en-us/library/a5s9345t.aspx
http://msdn.microsoft.com/en-us/library/25687bhx.aspx

On the last page, note the diagram of the stack frame for __stdcall
function. No evidence of total size, or any other additional information
beyond function arguments themselves. This is the "unexpanded"
definition.

>>> It could be elaborated on with additional notation like
>>>
>>> __declspec( nfrs_rgssz ) int __stdcall find( int what, ... );
>>>
>>> (darned if I can remember ordinary syntax for __declspec but
>>> something like that), and here the compiler would know that it
>>> doesn't need to pass the number of argument bytes because the
>>> function infers that from the arguments.
>>
>> But how would the function infer that? The completely automatic way
>> is "far fetched", as you noted.
>
> Yes, and the manual way is not.
>
> It is not at all difficult to /design/ foo so that e.g. first
> argument says how many arguments follow. Nor is it difficult to
> provide foo with a library routine it can call in order to get
> correct automatic stack cleanup prior to returning.

That's precisely the same as the compiler directive I said you would
need, the one you ridicule below. It doesn't matter if this directive
comes in the form of a library routine or otherwise. I meant "directive"
in the sense "some manual way to direct the compiler", not implying any
particular syntax.

> It would not be as safe as automated passing of nr. of bytes, though.
> Using a directive such as above would be back down at the unsafety
> level of cdecl, just in order to shave off one caller instruction.

Now that you mention safety, I think I understand the case your
mechanism is supposed to help with. You might be thinking of something
like printf("%d"). If the caller passes total size of arguments to the
callee, va_arg could be instrumented to check that it doesn't reach
beyond those arguments.

While it might help diagnose one particular way to misuse printf, it
won't help with others, e.g. printf("%s", 123). I guess it is possible
for the caller to prepare and pass a complete description of actual
argument types, and for va_arg to verify that it's used in accordance
with this description. That could be a valuable debugging aid, but the
overhead would probably be too high for production code.

Are there other scenarios where cdecl is less safe than stdcall (whether
"traditional" or "expanded")?

>> Presumably there would have to be some
>> compiler directive whereby the programmer would manually tell the
>> compiler how many arguments he thinks the function was passed.
>
> Huh. At this point I think you're into the old habit of speculating
> about impractical, undoable schemes again.

My hypothetical compiler directive is no different than your
hypothetical library routine. Both are supposed to achieve the same
goal, and neither currently exists.

Igor Tandetnik

unread,
Jul 20, 2008, 12:48:28 PM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:25WdnXEUbI2EwR_V...@posted.comnet

> * Alex Blekhman:
>>> You'd find decimal number system problematic if you came from an
>>> octal culture, being used to that.
>>
>> According to my understanding of what a calling convention
>> consists of, your example is beyond the limits of __stdcall
>> capabilities because it requires new convention about the meaning
>> of parameters.
>
> May be. But the point is that stdcall could easily have used this
> technique.

Perhaps it could, but it doesn't ("not supported by tools").

> Hence it's incorrect to say that stdcall cannot or could
> not support variadic number of arguments (as Igor did)

I only stated that stdcall, *as it exists today*, cannot support
variadic functions. Microsoft agrees:

http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

"The __stdcall calling convention is used to call Win32 API functions.
The callee cleans the stack, so the compiler makes vararg functions
__cdecl."

I never stated that no modification of stdcall can possibly exist that
could support variadic functions (but it does appear to me that a few
such modifications I could think of are no better at supporting variadic
functions than an existing cdecl already is).

It could be argued that any modification to existing calling
convention - whether for the purpose of supporting variadic functions or
otherwise - should be given a different name (as was done with
__thiscall). You insist on calling it stdcall - and that's OK with me,
as long as you distinguish between stdcall as it is currently
implemented by VC compiler, and your hypothetical modified stdcall that
"could easily" exist (but, to the best of my knowledge, doesn't).

> So it's relative, as most things are. ;-)

And thus perhaps calling participants that hold a different point of
view "stupid" or "liars" might not be such a good idea as it probably
seemed at the time. These terms sound pretty absolute to me.

> I just wish the other two participants in this thread would stop
> posting inane, idiotic, meaningless articles, engaging my
> calling-names circuits all the time (or perhaps that's why they do
> it, if so then it's not very nice of them).

Case in point.

Alf P. Steinbach

unread,
Jul 20, 2008, 3:10:47 PM7/20/08
to
* Igor Tandetnik:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:nNmdnc68-dIuqB_V...@posted.comnet
>> * Igor Tandetnik:
>>>> Essentially then upon seeing
>>>>
>>>> int __stdcall find( int what, ... );
>>>>
>>>> all the compiler needs to remember is to, in its own way, pass the
>>>> number of argument bytes
>>> You seem to be proving my point. The only reason stdcall was
>>> invented in the first place, is because it shaves one machine
>>> instruction off the call site (the "add esp, <size of arguments>"
>>> one) compared to cdecl.
>> I don't think it can be true that the designers reasoned that way
>
> Why can't it be true?
>
>> but it's possible -- and perhaps they've told you their reasoning?
>
> They did - there is a written account of it somewhere, but I must admit
> I can't find a reference at the moment.

OK, MS does move in mysterious ways.


> Let's state it this way: I personally claim, looking at the details of
> the two conventions, that the only advantage of stdcall over cdecl lies
> in removing one machine instruction from the call site.
>
>>> This is its one and only advantage over cdecl.
>> No, that is incorrect.
>
> In what way is that incorrect?

You mean "ways".

Let's first consider stdcall with fixed number and types of arguments. This
point is not important, but from academic point of view it is an extra
advantage, thus, sufficient to say your statement is not correct. The next two
points, for variable number of arguments, are of more practical significance,
but you may be unwilling to consider those points because current tools such as
Visual C++ do not implement those advantages (it is a tool limitation).

The academic point, with fixed number and types of args: with cdecl the function
needs to return to the caller to have the stack area used for arguments
deallocated. With stdcall convention, when the argument data is no longer needed
it can freely deallocate that area (by incrasing SP) and reuse it in calls to
other functions. Which might matter when that area is large -- it's not an
optimization that I know any compiler to do, and it's only applicable in some
corner cases (large stack area, and for in practice no local variables), but it
is an optimization that is available with stdcall, and not with cdecl.

Now let's consider stdcall with variable number of arguments and a function that
doesn't infer its other arguments from some known argument(s). In that case, the
requirements of stdcall /dictate/ that somehow the argument stack area is
passed: it is a direct logical consequence of documented stdcall requirements.

As a simple first example, consider then

void bar( ... );
void foo( ... ) { bar( someNotationForPassingOriginalArgs ); }

which includes the case of a recursive foo reusing args, and the special case of
a tail recursive foo reusing args.

Works simply and nicely with stdcall (whichever general convention is used to
deal with this within the constraints of stdcall convention), whereas with cdecl
would need special purpose modification -- a mechanism like the one for
stdcall, rendering the whole point of cdecl moot -- to do it.

As another and equally important example, with stdcall a function such as printf
has a means of checking that it has indeed been passed enough bytes for the
stated format specification: although the printf function is of a form such that
it doesn't know whether those bytes are the right number and types of arguments,
i.e. doesn't know enough to determine that the call is OK, it does know enough
to in many cases say the call isn't OK (as with MS' newfangled buffer length
checking string handling functions). I think I mentioned this earlier to you,
but perhaps was to other guy. Anyway is trivial consequence so should not be
hard to figure out on one's own.


>>> You are putting a
>>> different machine instruction right back into the call site,
>> In some cases, if needed, yes.
>>
>>> and calling
>>> it a new and improved stdcall.
>> No, both of those are lies. I.e., 2 lies in that short little
>> snippet of text. But of course you may be thinking that those are
>> not lies but accurate descriptions of reality, to the extent that
>> your memory permits you to perceive reality and not a distorted
>> version of it. In that case, please try to provide textual quotes
>> where I (1) call that a "new stdcall" (or imply that, or similar),
>> and (2) call that an "improved stdcall" (or imply that, or similar).
>
> By all means. The technique you propose is different from "traditional"
> __stdcall as currently implemented by VC compiler: "not supported by
> tools". Are you suggesting there exists, or used to exist, a compiler
> that implemented stdcall the way you describe? If so, could you cite a
> reference? Otherwise, the technique is properly characterized as "new",
> as in "never before implemented".

Oh, thanks. Yes, it's possible, although doubtful, that the technique is new in
the Microsoft world. However, it conforms fully to the requirements on stdcall
as documented at e.g. <url:
http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.71).aspx>.


> You said your technique would support variadic functions. The
> "traditional" stdcall doesn't (again, "not supported by tools"). I
> assumed you would claim this fact as an improvement. However, if you
> don't maintain your technique is an improvement, then we are in
> agreement. It's been my point all along that the mechanism you propose,
> while possible, is inferior to existing alternatives.

As I've stated I don't think it's in practice an improvement. However, the
technique is probably not inferior to cdecl. Since cdecl can always be used as
an alternative except for e.g. the two cases discussed above, which anyway
aren't supported today, what matters in practice is speed and size, where only
measurements can tell, and then perhaps not even in general but just for
specific applications and contexts. I think it would *probably* come out the
winner on both counts, and furthermore supports more functionality and better
safety -- but that latter property counts against it, because with quality of
programmers what it is, they're then more likely to use unsafe constructs such
as "...", like hey, now it's "safe". Your characterization as "inferior" is
however not backed up by any argument, and given your question above about
advantages I think you haven't even considered safety, and so I think you're
simply flinging adjectives about -- that you're not referring to the improved
safety as possibly backfiring, or indeed to anything, just an unfounded stance.


>>> It seems to be an argument over terminology. You seem to say:
>>> stdcall is any arrangement, no matter how complicated, where the
>>> callee, rather than the caller, ends up cleaning the stack. I
>>> concede: under this expanded definition of stdcall, it is possible
>>> to have a stdcall variadic function.
>> How can you say that Microsoft's own definition of stdcall is an
>> expanded one?
>> What is the /unexpanded/ (in your view) definition of stdcall?
>
> Microsoft's own definition of stdcall doesn't involve passing total size
> of arguments to the callee. Your does, at least "in some cases". In this
> sense yours is "expanded".

OK, but that's just a word game.


>> Please provide a link to such unexpanded definition.
>
> http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx
> http://msdn.microsoft.com/en-us/library/a5s9345t.aspx
> http://msdn.microsoft.com/en-us/library/25687bhx.aspx
>
> On the last page, note the diagram of the stack frame for __stdcall
> function. No evidence of total size, or any other additional information
> beyond function arguments themselves.

He he, ROTFL. Have you consider a register? Not that concrete examples that do
not illustrate the relevant context, are relevant in any way (disclaimer: I
haven't looked at these examples, confident that they're not relevant).


> This is the "unexpanded"
> definition.
>
>>>> It could be elaborated on with additional notation like
>>>>
>>>> __declspec( nfrs_rgssz ) int __stdcall find( int what, ... );
>>>>
>>>> (darned if I can remember ordinary syntax for __declspec but
>>>> something like that), and here the compiler would know that it
>>>> doesn't need to pass the number of argument bytes because the
>>>> function infers that from the arguments.
>>> But how would the function infer that? The completely automatic way
>>> is "far fetched", as you noted.
>> Yes, and the manual way is not.
>>
>> It is not at all difficult to /design/ foo so that e.g. first
>> argument says how many arguments follow. Nor is it difficult to
>> provide foo with a library routine it can call in order to get
>> correct automatic stack cleanup prior to returning.
>
> That's precisely the same as the compiler directive I said you would
> need, the one you ridicule below.

Sorry, then I misunderstood you.

But anyway, the directive's only needed for effecting a silly micro-optimization
(perhaps I shouldn't have discussed it at all, details tend to obscure the full
view).

It's not needed for general case.


> It doesn't matter if this directive
> comes in the form of a library routine or otherwise. I meant "directive"
> in the sense "some manual way to direct the compiler", not implying any
> particular syntax.
>
>> It would not be as safe as automated passing of nr. of bytes, though.
>> Using a directive such as above would be back down at the unsafety
>> level of cdecl, just in order to shave off one caller instruction.
>
> Now that you mention safety, I think I understand the case your
> mechanism is supposed to help with. You might be thinking of something
> like printf("%d"). If the caller passes total size of arguments to the
> callee, va_arg could be instrumented to check that it doesn't reach
> beyond those arguments.

No, the mechanism is not supposed to help with that, it just emerges as a
distinct advantage. But yes, it seems that regarding what's possible here,
improved safety, we're now in agreement.


> While it might help diagnose one particular way to misuse printf, it
> won't help with others, e.g. printf("%s", 123).

Right.


> I guess it is possible
> for the caller to prepare and pass a complete description of actual
> argument types, and for va_arg to verify that it's used in accordance
> with this description. That could be a valuable debugging aid, but the
> overhead would probably be too high for production code.

Same as with MS's "safe" string functions.


> Are there other scenarios where cdecl is less safe than stdcall (whether
> "traditional" or "expanded")?

Don't know. Please don't make me think. :-)


>>> Presumably there would have to be some
>>> compiler directive whereby the programmer would manually tell the
>>> compiler how many arguments he thinks the function was passed.
>> Huh. At this point I think you're into the old habit of speculating
>> about impractical, undoable schemes again.
>
> My hypothetical compiler directive is no different than your
> hypothetical library routine. Both are supposed to achieve the same
> goal, and neither currently exists.

OK, I misunderstood what you meant about that compiler directive. But again,
note that it would only be a silly micro-optimization, not required.

Alf P. Steinbach

unread,
Jul 20, 2008, 3:25:08 PM7/20/08
to
* Igor Tandetnik:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:25WdnXEUbI2EwR_V...@posted.comnet
>> * Alex Blekhman:
>>>> You'd find decimal number system problematic if you came from an
>>>> octal culture, being used to that.
>>> According to my understanding of what a calling convention
>>> consists of, your example is beyond the limits of __stdcall
>>> capabilities because it requires new convention about the meaning
>>> of parameters.
>> May be. But the point is that stdcall could easily have used this
>> technique.
>
> Perhaps it could, but it doesn't ("not supported by tools").
>
>> Hence it's incorrect to say that stdcall cannot or could
>> not support variadic number of arguments (as Igor did)
>
> I only stated that stdcall, *as it exists today*, cannot support
> variadic functions.

I think what you mean is not that /stdcall/ /cannot/.

I think what you mean is /Visual C++/ /does not/.

Anwyay, the latter is correct, and the former is just nonsense.

> Microsoft agrees:
>
> http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx
>
> "The __stdcall calling convention is used to call Win32 API functions.
> The callee cleans the stack, so the compiler makes vararg functions
> __cdecl."

Microsoft does not say what you literally say.

But it might be that Microsoft says what you're intending to say.

:-)


> I never stated that no modification of stdcall can possibly exist that
> could support variadic functions (but it does appear to me that a few
> such modifications I could think of are no better at supporting variadic
> functions than an existing cdecl already is).
>
> It could be argued that any modification to existing calling
> convention - whether for the purpose of supporting variadic functions or
> otherwise - should be given a different name (as was done with
> __thiscall). You insist on calling it stdcall - and that's OK with me,
> as long as you distinguish between stdcall as it is currently
> implemented by VC compiler, and your hypothetical modified stdcall that
> "could easily" exist (but, to the best of my knowledge, doesn't).
>
>> So it's relative, as most things are. ;-)
>
> And thus perhaps calling participants that hold a different point of
> view "stupid" or "liars" might not be such a good idea as it probably
> seemed at the time. These terms sound pretty absolute to me.

Yes. When someone really annoys by being extraordinarily stupid I tend to say
so. But no, as I recall, wrt. you I chose to think you thickheaded/stupid,
which can be a temporal state, not an absolute property, rather than liar. Even
though in one example, which of course I pointed out, you did include 2 lies in
one very short snippet of text. On the other hand, prior to all of that, you did
effectively call me a whiner, by saying I had "complained" about something, and
as I recall also implying that in other words. You reap as you sow, my friend.

Just count yourself lucky that you didn't do that with some of the oldtimers
(those who to me are oldtimers).


>> I just wish the other two participants in this thread would stop
>> posting inane, idiotic, meaningless articles, engaging my
>> calling-names circuits all the time (or perhaps that's why they do
>> it, if so then it's not very nice of them).
>
> Case in point.

Yes. :-) Happily it doesn't matter to much to me, at least not in these
out-of-the-way Microsoft groups. It's been a very long time since last fresh
Usenet discussion, so this has been very enjoyable.

Liviu

unread,
Jul 20, 2008, 4:48:54 PM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Igor Tandetnik:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>
>>> Hence it's incorrect to say that stdcall cannot or could
>>> not support variadic number of arguments (as Igor did)
>>
>> I only stated that stdcall, *as it exists today*, cannot support
>> variadic functions.
>
> I think what you mean is not that /stdcall/ /cannot/.
>
> I think what you mean is /Visual C++/ /does not/.

You seem to rejoice in the misconception that stdcall is a C-only thing
(and, this one must be new, that VC++ is the only relevant compiler).

Any "extension" to the existing stdcall convention which breaks binary
compatibility is _not_ and can _not_ claim to be stdcall.

> Anwyay, the latter is correct, and the former is just nonsense.

Talk is cheap, again. Please go back to that assembler snippet I posted
earlier. If you can't fill in the _printf code in such a way that (a) it
doesn't corrupt the stack, and (b) doesn't require modifications in the
caller code, then whatever that is which you propose it's _not_ stdcall.

> You reap as you sow, my friend.

You reaped a lot of sense and patience for the noise you sowed.

Cheers,
Liviu

Alf P. Steinbach

unread,
Jul 20, 2008, 5:30:01 PM7/20/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote
>> * Igor Tandetnik:
>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>
>>>> Hence it's incorrect to say that stdcall cannot or could
>>>> not support variadic number of arguments (as Igor did)
>>> I only stated that stdcall, *as it exists today*, cannot support
>>> variadic functions.
>> I think what you mean is not that /stdcall/ /cannot/.
>>
>> I think what you mean is /Visual C++/ /does not/.
>
> You seem to rejoice in the misconception that stdcall is a C-only thing
> (and, this one must be new, that VC++ is the only relevant compiler).
>
> Any "extension" to the existing stdcall convention which breaks binary
> compatibility is _not_ and can _not_ claim to be stdcall.

I'm sorry, that's not meaningful.

First, we're not talking about an extension: no constraints of stdcall are
broken, so it /is/, by the definition of stdcall, stdcall, as evidenced by the
calls working just as they should with stdcall calling convention.

Second, adopting this technique does not break any binary compatibility unless
it's done by someone incompetent.

Third, the conclusion does not follow, and is incorrect.

So you got both the facts and the reasoning and the conclusion wrong, for the
umpteenth' time.


>> Anwyay, the latter is correct, and the former is just nonsense.
>
> Talk is cheap, again. Please go back to that assembler snippet I posted
> earlier. If you can't fill in the _printf code in such a way that (a) it
> doesn't corrupt the stack, and (b) doesn't require modifications in the
> caller code, then whatever that is which you propose it's _not_ stdcall.

The assembler snippet you posted was a challenge to deduce a caller's supplied
arguments in the absence of any information about them.

A similar challenge is here, assuming:

xor eax, eax
; Here deduce the former contents of eax

If you cannot do this then what does that show?

An unrelated impossible example, an example of how to incompetently implement
something in a way that does not work, does not show anything technical. Your
example is unrelated and does not show anything, other than your debating
technique and general lack of insight, and that you're able to incompetently
implement something in a way that does not work. You may provide ten thousand
examples showing off your ability to implement things in ways that do not work,
and it would still not show anything but your ability to make things not work.


Cheers, & hth.,

Igor Tandetnik

unread,
Jul 20, 2008, 6:44:29 PM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:VKednQ8L0_gmEB7V...@posted.comnet
> * Igor Tandetnik:

>> Let's state it this way: I personally claim, looking at the details
>> of the two conventions, that the only advantage of stdcall over
>> cdecl lies in removing one machine instruction from the call site.
>>
>>>> This is its one and only advantage over cdecl.
>>> No, that is incorrect.
>>
>> In what way is that incorrect?
>
> You mean "ways".
>
> Let's first consider stdcall with fixed number and types of
> arguments. This point is not important, but from academic point of
> view it is an extra advantage, thus, sufficient to say your statement
> is not correct. The next two points, for variable number of
> arguments, are of more practical significance, but you may be
> unwilling to consider those points because current tools such as
> Visual C++ do not implement those advantages (it is a tool
> limitation).
> The academic point, with fixed number and types of args: with cdecl
> the function needs to return to the caller to have the stack area
> used for arguments deallocated. With stdcall convention, when the
> argument data is no longer needed it can freely deallocate that area
> (by incrasing SP) and reuse it in calls to other functions.

So can cdecl, as long as at the end it leaves ESP as it found it:

; arguments no longer needed


add esp, <size of arguments>

call otherFunction
sub esp, <size of arguments>

> Which
> might matter when that area is large -- it's not an optimization
> that I know any compiler to do, and it's only applicable in some
> corner cases (large stack area, and for in practice no local
> variables), but it is an optimization that is available with stdcall,
> and not with cdecl.

That is incorrect. As I've shown, it is available with cdecl also.

> Now let's consider stdcall with variable number of arguments and a
> function that doesn't infer its other arguments from some known
> argument(s). In that case, the requirements of stdcall /dictate/ that
> somehow the argument stack area is passed: it is a direct logical
> consequence of documented stdcall requirements.

Well, documented stdcall requirements state that it can't be used for
variadic functions in the first place, but I'll let it slide. I'm
assuming you are talking about your modified stdcall.

> As a simple first example, consider then
>
> void bar( ... );
> void foo( ... ) { bar( someNotationForPassingOriginalArgs ); }
>
> which includes the case of a recursive foo reusing args, and the
> special case of a tail recursive foo reusing args.

I'm not sure I follow. Tail recursion is usually eliminated by simply
jmp-ing to the beginning of the function, not by mucking with stack
frames. That would work equally well in stdcall or cdecl function.
Basically, a tail recursion is rewritten as a loop - surely both stdcall
and cdecl functions can run loops.

And I don't see how non-tail recursion could reuse arguments. After all,
the original call would need to preserve some state in order to continue
with its work after the recursive call. How would it do that, while
allowing the recursive call to trample on its stack frame?

What is this someNotationForPassingOriginalArgs you are talking about?
Could you elaborate? Perhaps with an illustrative assembly sequence?

> Works simply and nicely with stdcall (whichever general convention is
> used to deal with this within the constraints of stdcall convention),
> whereas with cdecl would need special purpose modification -- a
> mechanism like the one for stdcall, rendering the whole point of
> cdecl moot -- to do it.

Again, it's not clear to me from your description exactly _what_ works
simply and nicely with stdcall, and doesn't with cdecl. Could you
demonstrate these simple and nice workings?

> As another and equally important example, with stdcall a function
> such as printf has a means of checking that it has indeed been passed
> enough bytes for the stated format specification: although the printf
> function is of a form such that it doesn't know whether those bytes
> are the right number and types of arguments, i.e. doesn't know enough
> to determine that the call is OK, it does know enough to in many
> cases say the call isn't OK (as with MS' newfangled buffer length
> checking string handling functions). I think I mentioned this earlier
> to you, but perhaps was to other guy.

I don't remember you mentioning it to me, but I do remember mentioning
it to you in the very post you are replying to.

I concede: your modified stdcall can catch some, but not all, misuses of
printf.

>> By all means. The technique you propose is different from
>> "traditional" __stdcall as currently implemented by VC compiler:
>> "not supported by tools". Are you suggesting there exists, or used
>> to exist, a compiler that implemented stdcall the way you describe?
>> If so, could you cite a reference? Otherwise, the technique is
>> properly characterized as "new", as in "never before implemented".
>
> Oh, thanks. Yes, it's possible, although doubtful, that the technique
> is new in the Microsoft world. However, it conforms fully to the
> requirements on stdcall as documented at e.g. <url:
> http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.71).aspx>.

That is incorrect. First, this page states, and I quote: "the compiler
makes vararg functions __cdecl". Second, it doesn't document that the
compiler is allowed, even in some cases, to place additional information
on the stack beyond arguments themselves. Any such leeway would have to
be documented so that various tools could agree on precise stack layout
(which is, after all, the purpose of a calling convention).

>> You said your technique would support variadic functions. The
>> "traditional" stdcall doesn't (again, "not supported by tools"). I
>> assumed you would claim this fact as an improvement. However, if you
>> don't maintain your technique is an improvement, then we are in
>> agreement. It's been my point all along that the mechanism you
>> propose, while possible, is inferior to existing alternatives.
>
> As I've stated I don't think it's in practice an improvement.

Great. So we _are_ in agreement after all.

> However, the technique is probably not inferior to cdecl.

Well, it is in at least some respects. The call site is same size, but
the callee's epilogue is more involved for modified stdcall.

Perhaps there are other aspects where modified stdcall is better than
cdecl, but I have yet to see a convincing example of those (besides
printf guarding against stack overrun, which in my personal opinion is
not worth the bother: yours of course may differ).

> Since cdecl
> can always be used as an alternative except for e.g. the two cases
> discussed above, which anyway aren't supported today, what matters in
> practice is speed and size, where only measurements can tell, and
> then perhaps not even in general but just for specific applications
> and contexts. I think it would *probably* come out the winner on both

> counts.

Since the call site is same size, and the function body is strictly
larger and more complicated, I don't see under what set of circumstances
modified stdcall can ever win such a benchmark. Definitely not by size,
which is predictable. Could you explain how modified stdcall can be
faster than cdecl, even theoretically? What combination of caching,
branch prediction or other arcane factors could possibly help it out? I
suspect (but don't have any proof, before you ask) that existing CPUs
are carefully optimized for existing calling conventions, rather than
hypothetical ones.

> and furthermore supports more functionality and better safety

Are you talking about printf size checking case? I guess it counts as
better safety (though not by much), but more functionality? I've yet to
see an example of that.

> Your
> characterization as "inferior" is however not backed up by any
> argument

That is incorrect. I have mentioned it many times before: the call site
is same size, but the function epilogue is larger with modified stdcall
than it is with cdecl.

> and given your question above about advantages I think you
> haven't even considered safety

I must admit I haven't before you mentioned it (I guess you could have
mentioned it before resorting to insults, but whatever). Having seen
your printf example, I'm not convinced it's much of an improvement,
though I concede it is some improvement.

> and so I think you're simply flinging
> adjectives about -- that you're not referring to the improved
> safety as possibly backfiring, or indeed to anything, just an
> unfounded stance.

I was referring to a point I had made many times previously in this
thread, and which you conveniently chose not to respond to. Here it is
again: the call site is same size, but the function epilogue is larger
with modified stdcall than it is with cdecl.

>>>> It seems to be an argument over terminology. You seem to say:
>>>> stdcall is any arrangement, no matter how complicated, where the
>>>> callee, rather than the caller, ends up cleaning the stack. I
>>>> concede: under this expanded definition of stdcall, it is possible
>>>> to have a stdcall variadic function.
>>> How can you say that Microsoft's own definition of stdcall is an
>>> expanded one?
>>> What is the /unexpanded/ (in your view) definition of stdcall?
>>
>> Microsoft's own definition of stdcall doesn't involve passing total
>> size of arguments to the callee. Your does, at least "in some
>> cases". In this sense yours is "expanded".
>
> OK, but that's just a word game.

So is claiming that your "not supported by tools" stdcall is the same
calling convention as the "supported by tools" variety. Like I said,
it's an argument over terminology. Everything is relative, right?

>>> Please provide a link to such unexpanded definition.
>>
>> http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx
>> http://msdn.microsoft.com/en-us/library/a5s9345t.aspx
>> http://msdn.microsoft.com/en-us/library/25687bhx.aspx
>>
>> On the last page, note the diagram of the stack frame for __stdcall
>> function. No evidence of total size, or any other additional
>> information beyond function arguments themselves.
>
> He he, ROTFL. Have you consider a register?

Yes I have. The calling convention documents the use of registers, too.
Consider __thiscall and __fastcall, shown on this same page.

> Not that concrete
> examples that do not illustrate the relevant context, are relevant in
> any way (disclaimer: I haven't looked at these examples, confident
> that they're not relevant).

Well, given that we are talking about variadic functions, and that the
__stdcall documentation (the first cited page) says ""the compiler makes
vararg functions __cdecl", it's hard to expect an example of something
that's explicitly not supported.

>>> It is not at all difficult to /design/ foo so that e.g. first
>>> argument says how many arguments follow. Nor is it difficult to
>>> provide foo with a library routine it can call in order to get
>>> correct automatic stack cleanup prior to returning.
>>
>> That's precisely the same as the compiler directive I said you would
>> need, the one you ridicule below.
>
> Sorry, then I misunderstood you.

I wonder if, in the future, before calling people stupid, you might stop
and consider that perhaps you have misunderstood something they said. As
you see, it's a possiblity.

> But anyway, the directive's only needed for effecting a silly
> micro-optimization (perhaps I shouldn't have discussed it at all,
> details tend to obscure the full view).

Well, it is precisely this micro-optimization that makes
currently-existing stdcall an improvement over cdecl. If you drop it,
then my argument, about modified stdcall being worse than cdecl
code-size-wise, stands.

>> Now that you mention safety, I think I understand the case your
>> mechanism is supposed to help with. You might be thinking of
>> something like printf("%d"). If the caller passes total size of
>> arguments to the callee, va_arg could be instrumented to check that
>> it doesn't reach beyond those arguments.
>
> No, the mechanism is not supposed to help with that, it just emerges
> as a distinct advantage. But yes, it seems that regarding what's
> possible here, improved safety, we're now in agreement.

Could you give another example where safety is improved by using
modified stdcall over cdecl?

>> I guess it is possible
>> for the caller to prepare and pass a complete description of actual
>> argument types, and for va_arg to verify that it's used in accordance
>> with this description. That could be a valuable debugging aid, but
>> the overhead would probably be too high for production code.
>
> Same as with MS's "safe" string functions.

Well, those don't quite go to _these_ lengths. They just take a buffer
size along with the buffer pointer: error checking is straightforward.
Many people consider them plenty fast for production code. Windows OS
source code itself reportedly uses them:

http://msdn.microsoft.com/en-us/library/ms995349.aspx
http://download.microsoft.com/download/8/6/5/8659f5ec-6eaa-4b1f-9107-3e1ec9edf39c/secure_platform.doc

(search for "string handling"). At the very least, Microsoft is
definitely pushing them for use in production code, not just for
debugging.

>> Are there other scenarios where cdecl is less safe than stdcall
>> (whether "traditional" or "expanded")?
>
> Don't know. Please don't make me think. :-)

Ah. So you state a claim, but decline to back it with any argument.
Isn't that the same sin you often accuse me of?

Hmm, I wonder what one might call a person who refuses to think. Perhaps
one or more of the terms you used to describe me might fit?

Igor Tandetnik

unread,
Jul 20, 2008, 7:19:30 PM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:1rednZeHX8qKDB7V...@posted.comnet
> * Igor Tandetnik:

>> I only stated that stdcall, *as it exists today*, cannot support
>> variadic functions.
>
> I think what you mean is not that /stdcall/ /cannot/.
>
> I think what you mean is /Visual C++/ /does not/.

My position, of course, is that what Visual C++ does and documents is,
by definition, stdcall. Any modification of that is something else. We
have already discussed this at length.

> Anwyay, the latter is correct, and the former is just nonsense.

Now here's a strong, well-reasoned argument. It's so much easier to
dismiss an inconvenient point as "nonsense" than to rebut it.

>> And thus perhaps calling participants that hold a different point of
>> view "stupid" or "liars" might not be such a good idea as it probably
>> seemed at the time. These terms sound pretty absolute to me.
>
> Yes. When someone really annoys by being extraordinarily stupid I
> tend to say so. But no, as I recall, wrt. you I chose to think you
> thickheaded/stupid, which can be a temporal state, not an absolute
> property, rather than liar. Even though in one example, which of
> course I pointed out, you did include 2 lies in one very short
> snippet of text.

Which, upon futher scrutiny, turned out not to be false. I wonder if
holding my breath waiting for an apology might not be such a good idea.

> On the other hand, prior to all of that, you did
> effectively call me a whiner, by saying I had "complained" about
> something, and as I recall also implying that in other words. You
> reap as you sow, my friend.

I believed you suggested that your modified stdcall is a good idea (you
seem to still be arguing this elsewhere in this thread), and expressed
regret that it's not supported by existing tools. I used "complain" in
the sense of "express unhappiness", not meaning any negative
connotations.

http://wordnet.princeton.edu/perl/webwn?s=complain

complain: express complaints, discontent, displeasure, or unhappiness

But perhaps it was a bad choice of words on my part, and for that I
sincerely apologize.

> Just count yourself lucky that you didn't do that with some of the
> oldtimers (those who to me are oldtimers).

Do they routinely seek out and beat up pesky kids like me?

Alf P. Steinbach

unread,
Jul 20, 2008, 9:15:24 PM7/20/08
to
This is going far out in details about matters not related to the original error
you made, when stating that stdcall can't support variadic functions.

I'm trying to answer as best I can, but I think if you continue this you will
sooner or later find something unrelated to original issue that I don't know (I
don't know all).

So what have you then achieved?


* Igor Tandetnik:

I think you're right, and this was an incorrect example. Mea culpa. It would
hold for variable number of arguments, but not for fixed number of arguments.


>> Which
>> might matter when that area is large -- it's not an optimization
>> that I know any compiler to do, and it's only applicable in some
>> corner cases (large stack area, and for in practice no local
>> variables), but it is an optimization that is available with stdcall,
>> and not with cdecl.
>
> That is incorrect. As I've shown, it is available with cdecl also.
>
>> Now let's consider stdcall with variable number of arguments and a
>> function that doesn't infer its other arguments from some known
>> argument(s). In that case, the requirements of stdcall /dictate/ that
>> somehow the argument stack area is passed: it is a direct logical
>> consequence of documented stdcall requirements.
>
> Well, documented stdcall requirements state that it can't be used for
> variadic functions in the first place, but I'll let it slide.

No you shouldn't let that slide. I'm not familiar with any such documented
requirements. There is documentation of the Visual C++ "__stdcall" keyword, that
I read as that it doesn't implement stdcall but instead cdecl calling convention
when applied to variadic function. If you read it differently then you really
have to decide, as described AB below. Because then you have self-contradiction.


> I'm assuming you are talking about your modified stdcall.

I'm talking about stdcall calling convention, implemented any way that works.
:-) One way is the way I illustrated by a complete working example.

>> As a simple first example, consider then
>>
>> void bar( ... );
>> void foo( ... ) { bar( someNotationForPassingOriginalArgs ); }
>>
>> which includes the case of a recursive foo reusing args, and the
>> special case of a tail recursive foo reusing args.
>
> I'm not sure I follow. Tail recursion is usually eliminated by simply
> jmp-ing to the beginning of the function, not by mucking with stack
> frames. That would work equally well in stdcall or cdecl function.
> Basically, a tail recursion is rewritten as a loop - surely both stdcall
> and cdecl functions can run loops.

Yes, both can handle tail recursion. The point of mentioning that was just that
also stdcall can handle tail recursion efficiently in this particular case. It
seems I must stop mentioning details in advance.


> And I don't see how non-tail recursion could reuse arguments. After all,
> the original call would need to preserve some state in order to continue
> with its work after the recursive call. How would it do that, while
> allowing the recursive call to trample on its stack frame?

With stdcall the function knows the size of the argument area on the stack.

All it needs to do is to either copy that area or reuse it, depending on what
state on the stack (local variables below) it needs to preserve or not.

The cdecl variant can't copy the area, nor reuse it, because it doesn't know the
size of the area.


> What is this someNotationForPassingOriginalArgs you are talking about?
> Could you elaborate? Perhaps with an illustrative assembly sequence?

Current C or C++ do not have notation for argument forwarding. C++0x will have
such notation, specific syntax for argument forwarding, but I'm not familiar
with it (IIRC there is a g++ implementation), and I don't know whether it would
applicable here, although probably it would be.

At the assembly level it's very easy, at least conceptually. E.g.

__stdcall void bar( int x, ... ) { /* do things */ }

__stdcall double foo( int x, ... )
{
double y;
char z;

y = 3.14;
bar( x, someNotationForPassingOriginalArgs ); // X
return y;
}

Let's say this 32-bit x86 code, with usual prolog

push ebp
mov ebp, esp

Let's further say the number of bytes of arguments is passed in via ECX.

Then at point X all that's needed is to push x, and copy ECX-4 bytes from
[EBP+8] to new stack frame with corresponding adjustment of ESP, and call bar.

An optimizing compiler might optimize this in various ways.

E.g., since it knows x is not modified there's no need to treat x separately, it
can just copy ECX bytes.

And if it knows the stack requirements of bar, and that the contents in the
incoming arguments area are not significant after that call, it can forego the
whole copying by just making room for bar's stack requirement, placing y and z
below, and then efficiently reuse the area.

>> Works simply and nicely with stdcall (whichever general convention is
>> used to deal with this within the constraints of stdcall convention),
>> whereas with cdecl would need special purpose modification -- a
>> mechanism like the one for stdcall, rendering the whole point of
>> cdecl moot -- to do it.
>
> Again, it's not clear to me from your description exactly _what_ works
> simply and nicely with stdcall, and doesn't with cdecl. Could you
> demonstrate these simple and nice workings?

See above.

cdecl doesn't have enough information.

Or if it's given that information, then it just has overhead compared to
stdcall, and no advantage.


>> As another and equally important example, with stdcall a function
>> such as printf has a means of checking that it has indeed been passed
>> enough bytes for the stated format specification: although the printf
>> function is of a form such that it doesn't know whether those bytes
>> are the right number and types of arguments, i.e. doesn't know enough
>> to determine that the call is OK, it does know enough to in many
>> cases say the call isn't OK (as with MS' newfangled buffer length
>> checking string handling functions). I think I mentioned this earlier
>> to you, but perhaps was to other guy.
>
> I don't remember you mentioning it to me, but I do remember mentioning
> it to you in the very post you are replying to.
>
> I concede: your modified stdcall can catch some, but not all, misuses of
> printf.

Good, except it's not modified. There's no modification of stdcall requirements.


>>> By all means. The technique you propose is different from
>>> "traditional" __stdcall as currently implemented by VC compiler:
>>> "not supported by tools". Are you suggesting there exists, or used
>>> to exist, a compiler that implemented stdcall the way you describe?
>>> If so, could you cite a reference? Otherwise, the technique is
>>> properly characterized as "new", as in "never before implemented".
>> Oh, thanks. Yes, it's possible, although doubtful, that the technique
>> is new in the Microsoft world. However, it conforms fully to the
>> requirements on stdcall as documented at e.g. <url:
>> http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.71).aspx>.
>
> That is incorrect. First, this page states, and I quote: "the compiler
> makes vararg functions __cdecl".

That "incorrect" is an invalid inference.

You need to decide:

Whether

A) You regard a variadic function declared __stdcall as having stdcall
calling convention.

In this case your argument that stdcall doesn't support variadic
function is contradicted, because you're then saying it does, and
that stdcall convention for variadic function means the same as
with cdecl.

or

B) You regard a variadic function declared __stdcall as not having
stdcall calling convention, but cdecl calling convention.

In this case your quote is completely irrelevant to what's done in the
stdcall convention.


> Second, it doesn't document that the
> compiler is allowed, even in some cases, to place additional information
> on the stack beyond arguments themselves.

First, I think you mean "pass additional information", since a requirement to
pass that information on the stack would be stupid.

Now, if this argument was valid (which it isn't) then RVO optimization would be
prohibited for stdcall functions, as it does pass additional information.

For example, with

struct Foo{ int x; Foo(): x(42) {} };

Foo __stdcall blah() { Foo o; return o; }

int main()
{
Foo o = blah();
std::cout << o.x << std::endl;
}

in an ordinary debug build the Visual C++ compiler (which is what the
documentation refers to) adds a hidden argument, the address of 'o', in register
eax.

It does that even if blah() is just declared and defined in another file.


> Any such leeway would have to
> be documented so that various tools could agree on precise stack layout
> (which is, after all, the purpose of a calling convention).

Hm, that's a mixture of good and bad in same sentence.

Let's take the bad first. _stdcall is a calling convention that applies to e.g.
functions like blah() above. I hope you don't disagree with that. If you do
disagree that this __stdcall function is a stdcall function then a few weeks
debate on the contexts in which __stdcall really denotes stdcall may be ahead...

When such a function has arguments or result of a type that can vary between C++
compilers or with various options even on given OS, then it cannot in general be
called, without adding in low-level shenanigans, from source code compiled with
any other compiler or with incompatible options. Thus a calling convention only
supplies interoperability to the degree that languages and their implementations
already allow that interoperability. And in particular, it does not impose a
precise stack layout, for if it did then it would, e.g., exclude most of C++.

So the "Any such leeway" is invalid (take a look at above RVO code again).

On the other hand, in order to adopt such a technique it would be most practical
if the OS vendor, Microsoft, did document their version. They could even call it
a new calling convention, whatever. Then it would be possible for other vendors
to e.g. supply stdcall printf and have it consumed by MS compiler.

On the third hand, this is talking about hypotheticals, and really assumes that
stdcall can handle variadic functions (which it can, as demonstrated).


>>> You said your technique would support variadic functions. The
>>> "traditional" stdcall doesn't (again, "not supported by tools"). I
>>> assumed you would claim this fact as an improvement. However, if you
>>> don't maintain your technique is an improvement, then we are in
>>> agreement. It's been my point all along that the mechanism you
>>> propose, while possible, is inferior to existing alternatives.
>> As I've stated I don't think it's in practice an improvement.
>
> Great. So we _are_ in agreement after all.

On that point, but I suspect for different reasons.


>> However, the technique is probably not inferior to cdecl.
>
> Well, it is in at least some respects. The call site is same size, but
> the callee's epilogue is more involved for modified stdcall.
>
> Perhaps there are other aspects where modified stdcall is better than
> cdecl, but I have yet to see a convincing example of those (besides
> printf guarding against stack overrun, which in my personal opinion is
> not worth the bother: yours of course may differ).

That's turning things on their head. First you're denying that stdcall can
handle variadic functions, but being taken up on that, now you require me to
/convince/ you that it's more efficient. Bah.


>> Since cdecl
>> can always be used as an alternative except for e.g. the two cases
>> discussed above, which anyway aren't supported today, what matters in
>> practice is speed and size, where only measurements can tell, and
>> then perhaps not even in general but just for specific applications
>> and contexts. I think it would *probably* come out the winner on both
>> counts.
>
> Since the call site is same size, and the function body is strictly
> larger and more complicated, I don't see under what set of circumstances
> modified stdcall can ever win such a benchmark. Definitely not by size,
> which is predictable. Could you explain how modified stdcall can be
> faster than cdecl, even theoretically? What combination of caching,
> branch prediction or other arcane factors could possibly help it out?

Simply by having available more information it permits optimizations to be made,
because optimizations are in the end only ways to exploit available information
(e.g. cache this chunk of stack where I have my arguments). But whether such
optimizations would be made is purely hypothetical. Whether this level of
micro-optimization matters is highly doubtful. And so on. I see this line of
questioning as just a way to go off on a tangent.


> I
> suspect (but don't have any proof, before you ask) that existing CPUs
> are carefully optimized for existing calling conventions, rather than
> hypothetical ones.

Well I don't know and I don't care about the efficency; as I wrote above only
measurements can tell. If my speculation on that turns out to be wrong, or
right, so what? Why are you interested in micro-efficiency for a variadic
function, typically ineffecient anyway?


>> and furthermore supports more functionality and better safety
>
> Are you talking about printf size checking case? I guess it counts as
> better safety (though not by much), but more functionality? I've yet to
> see an example of that.

See earlier in this article.


>> Your
>> characterization as "inferior" is however not backed up by any
>> argument
>
> That is incorrect. I have mentioned it many times before: the call site
> is same size, but the function epilogue is larger with modified stdcall
> than it is with cdecl.

Well that's not something anyone objective and competent would use to measure
superiority or inferiority by. Anyway what does inferiority or superiority in
/any/ respect have to do with your error?


>> and given your question above about advantages I think you
>> haven't even considered safety
>
> I must admit I haven't before you mentioned it (I guess you could have
> mentioned it before resorting to insults, but whatever). Having seen
> your printf example, I'm not convinced it's much of an improvement,
> though I concede it is some improvement.

Agreed.

I don't really think adding safety at that low level improves safety.

Instead it might make it easier to resort to using that level instead of more
significantly safes higher levels.


[snip]

He he. Nothing there about registers in general, or EAX for RVO on stdcall
function... :-) But I looketh, and somehow ended up on <url:
http://msdn.microsoft.com/en-us/library/f9t8842e(VS.71).aspx>, documenting an
[.exe]'s entry point, as specified by linker /ENTRY, to have WinMain signature...

You know, this documentation isn't worth anything: it's not quality.

It's what we have to relate to, though, and for that you have to add simple
sound judgement, not take the incompetent tech-writer's word as gold, or, in
particular, as you do, think that's something not mentioned is forbidden.


>> Not that concrete
>> examples that do not illustrate the relevant context, are relevant in
>> any way (disclaimer: I haven't looked at these examples, confident
>> that they're not relevant).
>
> Well, given that we are talking about variadic functions, and that the
> __stdcall documentation (the first cited page) says ""the compiler makes
> vararg functions __cdecl", it's hard to expect an example of something
> that's explicitly not supported.

By Visual C++.

As mentioned, it's just a tool issue.


>>>> It is not at all difficult to /design/ foo so that e.g. first
>>>> argument says how many arguments follow. Nor is it difficult to
>>>> provide foo with a library routine it can call in order to get
>>>> correct automatic stack cleanup prior to returning.
>>> That's precisely the same as the compiler directive I said you would
>>> need, the one you ridicule below.
>> Sorry, then I misunderstood you.
>
> I wonder if, in the future, before calling people stupid, you might stop
> and consider that perhaps you have misunderstood something they said. As
> you see, it's a possiblity.

I wonder if in the future you could refrain from such insinuations?

I strive to only tell people they're being stupid when they're actually are
being stupid, and being told might help them: as I recall, in this case that
didn't apply.

In the case of your compiler directive I wrote that "At this point I think

you're into the old habit of speculating about impractical, undoable schemes

again." Instead it appears you were talking about micro-optimization, at least
if your explanation is correct. Then what you wrote, "there would have to be"
such an optimization doesn't make much sense, but so what.

>> But anyway, the directive's only needed for effecting a silly
>> micro-optimization (perhaps I shouldn't have discussed it at all,
>> details tend to obscure the full view).
>
> Well, it is precisely this micro-optimization that makes
> currently-existing stdcall an improvement over cdecl. If you drop it,
> then my argument, about modified stdcall being worse than cdecl
> code-size-wise, stands.

It doesn't exactly stand in the sense of being correct, but it's a possibility
wrt. speed, size or both. Not with respect to other things. Anyway, who cares
what the speed or size or relative merits are -- I for one absolutely don't.

We were discussing you error in stating that stdcall can't support variadic
functions.

For that, the micro-efficiency or micro-size of any solution is irrelevant.


>>> Now that you mention safety, I think I understand the case your
>>> mechanism is supposed to help with. You might be thinking of
>>> something like printf("%d"). If the caller passes total size of
>>> arguments to the callee, va_arg could be instrumented to check that
>>> it doesn't reach beyond those arguments.
>> No, the mechanism is not supposed to help with that, it just emerges
>> as a distinct advantage. But yes, it seems that regarding what's
>> possible here, improved safety, we're now in agreement.
>
> Could you give another example where safety is improved by using
> modified stdcall over cdecl?

Why?


>>> I guess it is possible
>>> for the caller to prepare and pass a complete description of actual
>>> argument types, and for va_arg to verify that it's used in accordance
>>> with this description. That could be a valuable debugging aid, but
>>> the overhead would probably be too high for production code.
>> Same as with MS's "safe" string functions.
>
> Well, those don't quite go to _these_ lengths. They just take a buffer
> size along with the buffer pointer: error checking is straightforward.
> Many people consider them plenty fast for production code. Windows OS
> source code itself reportedly uses them:
>
> http://msdn.microsoft.com/en-us/library/ms995349.aspx
> http://download.microsoft.com/download/8/6/5/8659f5ec-6eaa-4b1f-9107-3e1ec9edf39c/secure_platform.doc
>
> (search for "string handling"). At the very least, Microsoft is
> definitely pushing them for use in production code, not just for
> debugging.

How much less do you think passing a buffer length, as opposed to stack area
size, is? When you write "go to _these_ length". I just wonder.


>>> Are there other scenarios where cdecl is less safe than stdcall
>>> (whether "traditional" or "expanded")?
>> Don't know. Please don't make me think. :-)
>
> Ah. So you state a claim, but decline to back it with any argument.
> Isn't that the same sin you often accuse me of?

I'm pretty sure that's an attempt at insinuation, but if you would quote the
claim you think has been made then this can be discussed.


> Hmm, I wonder what one might call a person who refuses to think. Perhaps
> one or more of the terms you used to describe me might fit?

I'm refusing to do your thinking for you.

What do you call a person who asks others to do his thinking for him?


Cheers, & hth.,

Liviu

unread,
Jul 20, 2008, 9:26:28 PM7/20/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Igor Tandetnik:
>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>
>>>>> Hence it's incorrect to say that stdcall cannot or could
>>>>> not support variadic number of arguments (as Igor did)
>>>>
>>>> I only stated that stdcall, *as it exists today*, cannot support
>>>> variadic functions.
>>>
>>> I think what you mean is not that /stdcall/ /cannot/.
>>>
>>> I think what you mean is /Visual C++/ /does not/.
>>
>> You seem to rejoice in the misconception that stdcall is a C-only
>> thing (and, this one must be new, that VC++ is the only relevant
>> compiler).
>>
>> Any "extension" to the existing stdcall convention which breaks
>> binary compatibility is _not_ and can _not_ claim to be stdcall.
>
> I'm sorry, that's not meaningful.

| Also, you must appreciate that __stdcall is just the C name for a well
| defined binary contract between caller and callee. It is not a matter
| of compiler, and there doesn't even have to be one involved. I could
| technically write assembler, or direct machine bytecodes in debug.exe
| if I so wished, and the same calling convention would still apply.

Which exact part do yo find not clear?

> First, we're not talking about an extension: no constraints of stdcall
> are broken, so it /is/, by the definition of stdcall, stdcall, as
> evidenced by the calls working just as they should with stdcall
> calling convention.

Yes, your calls to knurre() do work without breaking any stdcall
constraints.

No, the same technique cannot be applied to calling (or writing)
variadic functions in general, such as a compliant printf() which
doesn't trash its stack when called with excess arguments.

Which leaves you with just that, a __knurre convention which can call
knurre().

> Second, adopting this technique does not break any binary
> compatibility unless it's done by someone incompetent.

What "this technique" might be is largely undefined at this point, since
you haven't really provided anything near a formal spec for what would
be your __knurre convention, other than a singular example.

Yet, in case you suggest that the __knurre convention could
(a) be extended to cover variadic functions in general, including those
like printf which must tolerate excess arguments, and
(b) do it in a stdcall binary compatible way that "does not break any
binary compatibility",
then that's provably wrong, regardless of any particular specs you may
have had in mind.

Point (a) would require additional context info to be passed into the
callee. Since stdcall defines no such mechanism and reserves no location
(hidden param, designated register, stack slot) to accommodate that
additional information, any place you'd choose to pass the info would
have been up for grabs under the existing stdcall convention, and may
have been legally used by any stdcall-compliant code written in the past
decade. All such affected code would then break, unless rebuilt with
"new tools" using the new convention. Thus !(b) q.e.d.

> Third, the conclusion does not follow, and is incorrect.

Does not follow? Are you by any chance saying that a convention which
_does_ break binary compatibilty with the existing stdcall should, in
fact, claim to _be_ stdcall?

>> Talk is cheap, again. Please go back to that assembler snippet
>

> The assembler snippet you posted was a challenge to deduce a caller's
> supplied arguments in the absence of any information about them.

That was to sink the point that your calling convention couldn't
implement a compliant printf while maintaining stdcall compatibility.
Quite obviously, it can't.

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 20, 2008, 9:57:53 PM7/20/08
to
* Igor Tandetnik:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:1rednZeHX8qKDB7V...@posted.comnet
>> * Igor Tandetnik:
>>> I only stated that stdcall, *as it exists today*, cannot support
>>> variadic functions.
>> I think what you mean is not that /stdcall/ /cannot/.
>>
>> I think what you mean is /Visual C++/ /does not/.
>
> My position, of course, is that what Visual C++ does and documents is,
> by definition, stdcall. Any modification of that is something else. We
> have already discussed this at length.
>
>> Anwyay, the latter is correct, and the former is just nonsense.
>
> Now here's a strong, well-reasoned argument. It's so much easier to
> dismiss an inconvenient point as "nonsense" than to rebut it.

My first posting here in response to you was complete rebuttal in the form of
working code, where a function called as stdcall convention was variadic.

You're denying a matter of fact.


>>> And thus perhaps calling participants that hold a different point of
>>> view "stupid" or "liars" might not be such a good idea as it probably
>>> seemed at the time. These terms sound pretty absolute to me.
>> Yes. When someone really annoys by being extraordinarily stupid I
>> tend to say so. But no, as I recall, wrt. you I chose to think you
>> thickheaded/stupid, which can be a temporal state, not an absolute
>> property, rather than liar. Even though in one example, which of
>> course I pointed out, you did include 2 lies in one very short
>> snippet of text.
>
> Which, upon futher scrutiny, turned out not to be false.

That's at best a misunderstanding on your part.


> I wonder if
> holding my breath waiting for an apology might not be such a good idea.
>> On the other hand, prior to all of that, you did
>> effectively call me a whiner, by saying I had "complained" about
>> something, and as I recall also implying that in other words. You
>> reap as you sow, my friend.
>
> I believed you suggested that your modified stdcall is a good idea (you
> seem to still be arguing this elsewhere in this thread),

I'm not arguing for that technique. I'm just responding to invalid arguments
against it, including the incorrect idea that it is in any way a modification of
stdcall requirements. Would be unnatural if I didn't follow up on follow ups to
my own posting.


> and expressed
> regret that it's not supported by existing tools. I used "complain" in
> the sense of "express unhappiness", not meaning any negative
> connotations.
>
> http://wordnet.princeton.edu/perl/webwn?s=complain
>
> complain: express complaints, discontent, displeasure, or unhappiness
>
> But perhaps it was a bad choice of words on my part, and for that I
> sincerely apologize.

Huh. I'll be damned. OK. Trouble is, I think it's much more honest and better to
say what one really means and thinks than to undermine a person or use
misdirection or insinuations or such. Still I apologize for the language, if it
has offended.


>> Just count yourself lucky that you didn't do that with some of the
>> oldtimers (those who to me are oldtimers).
>
> Do they routinely seek out and beat up pesky kids like me?

Yes, they did.

The most infamous such response (that I know of) was sent by private e-mail from
one such oldtimer to, I guess it was a newbie Usenet user; <url:
http://preview.tinyurl.com/5aetyf>. Now that guy did just about the same in
public as in private e-mail, and he was so "good" at taking apart those who
posted technically incorrect opinions that there are huge archives of his most
"eloquent" tirades, but he's not been posting much for a while.

I hope you don't feel sought out or beaten up, though.


Cheers, & hth.,

Alf P. Steinbach

unread,
Jul 20, 2008, 10:36:43 PM7/20/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote
>> * Liviu:
>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>> * Igor Tandetnik:
>>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>>
>>>>>> Hence it's incorrect to say that stdcall cannot or could
>>>>>> not support variadic number of arguments (as Igor did)
>>>>> I only stated that stdcall, *as it exists today*, cannot support
>>>>> variadic functions.
>>>> I think what you mean is not that /stdcall/ /cannot/.
>>>>
>>>> I think what you mean is /Visual C++/ /does not/.
>>> You seem to rejoice in the misconception that stdcall is a C-only
>>> thing (and, this one must be new, that VC++ is the only relevant
>>> compiler).
>>>
>>> Any "extension" to the existing stdcall convention which breaks
>>> binary compatibility is _not_ and can _not_ claim to be stdcall.
>> I'm sorry, that's not meaningful.
>

[misleading quote marks removed]


> Also, you must appreciate that __stdcall is just the C name for a well
> defined binary contract between caller and callee. It is not a matter
> of compiler, and there doesn't even have to be one involved. I could
> technically write assembler, or direct machine bytecodes in debug.exe
> if I so wished, and the same calling convention would still apply.

That's correct.


> Which exact part do yo find not clear?

Uhm, as mentioned, you had the facts wrong, the reasoning wrong, and the
conclusion wrong. And even though I didn't mention it, that makes the statement
less than clear. It might that you had trouble formulating what you thought.


>> First, we're not talking about an extension: no constraints of stdcall
>> are broken, so it /is/, by the definition of stdcall, stdcall, as
>> evidenced by the calls working just as they should with stdcall
>> calling convention.
>
> Yes, your calls to knurre() do work without breaking any stdcall
> constraints.

Ah.


> No, the same technique cannot be applied to calling (or writing)
> variadic functions in general, such as a compliant printf() which
> doesn't trash its stack when called with excess arguments.

That's demonstrably false. The C standard does not impose a calling convention.
Hence a C compiler is free to use any convention it wants, including stdcall.

And as you noted above the calling convention is not what any particular
compiler supports.

We're discussing what stdcall can (or "could") support, not, hopefully, what
Visual C++ supports today.

And as demonstrated all that's necessary is for the compiler to pass the number
of argument bytes, in some way, when it has a variadic function at hand.

I think it's no good using time on preparing a concrete example for you: you
could in principle easily do that yourself, based on the code I posted. But
outline: if you're the compiler, when you have a stdcall variadic function at
hand, to call, you push the arguments right to left, pass the total number of
argument bytes in some way, e.g. in a register, and then fire and forget.


> Which leaves you with just that, a __knurre convention which can call
> knurre().

See above; that conclusion is incorrect.


>> Second, adopting this technique does not break any binary
>> compatibility unless it's done by someone incompetent.
>
> What "this technique" might be is largely undefined at this point, since
> you haven't really provided anything near a formal spec for what would
> be your __knurre convention, other than a singular example.

"This technique" refers to using the stdcall convention and, for a variadic
function, if necessary pass the number of argument bytes in some way.

That's required by stdcall's requirement that the routine is informed of the
number of argument bytes.

Which in turn stems from the requirement to adjust the stack pointer by that
number of bytes.


> Yet, in case you suggest that the __knurre convention could
> (a) be extended to cover variadic functions in general, including those
> like printf which must tolerate excess arguments, and

I apologize if I have only suggested that.

To me it's a trivial consequence.


> (b) do it in a stdcall binary compatible way that "does not break any
> binary compatibility",
> then that's provably wrong, regardless of any particular specs you may
> have had in mind.

Not at all, that's incorrect.


> Point (a) would require additional context info to be passed into the
> callee.

Yes.


> Since stdcall defines no such mechanism and reserves no location
> (hidden param, designated register, stack slot) to accommodate that
> additional information, any place you'd choose to pass the info would
> have been up for grabs under the existing stdcall convention,

No, not at all. There are only two cases to consider.

Either (1) the language implementation already supports variadic stdcall
routines, in which case it necessarily already does this. :-)

Or, (2), it does not, in which case with the language implemention we're
considering extending there are no existing calls to variadic stdcall functions,
no existing such functions, and therefore all at the machine code level is free,
within the constraints of argument pushing order and stack adjustment.


> and may
> have been legally used by any stdcall-compliant code written in the past
> decade.

Nope, see above.


> All such affected code would then break, unless rebuilt with
> "new tools" using the new convention. Thus !(b) q.e.d.

He he. No, not all. I hope the division into two cases above makes this more clear.

However, for Visual C++ there's trouble at a somewhat higher level, namely the
use of the "__stdcall" keyword for variadic functions, which the Visual C++
compiler automatically (braindead decision) translates to cdecl convention.

If one wanted to introduce stdcall variadic functions this would, however, be
easy to fix, without breaking anything. E.g., preprocessor symbol, compiler
option, new keyword "__really" or just "__not_cdecl" ;-) -- whatever.


>> Third, the conclusion does not follow, and is incorrect.
>
> Does not follow? Are you by any chance saying that a convention which
> _does_ break binary compatibilty with the existing stdcall should, in
> fact, claim to _be_ stdcall?

No, I'm saying that if the premises in your sentence were true, the conclusion
that such a convention could not claim to be stdcall would not follow. In my
opinion it would not be stdcall. But if e.g. Microsoft said it was, and e.g. let
the selection be done from some context (they've done equally silly things),
then we could not argue with that, we'd just have to accept yet another MS-ism.


>>> Talk is cheap, again. Please go back to that assembler snippet
>> The assembler snippet you posted was a challenge to deduce a caller's
>> supplied arguments in the absence of any information about them.
>
> That was to sink the point that your calling convention couldn't
> implement a compliant printf while maintaining stdcall compatibility.

Well, the example was completely irrelevant. It seems, please correct me if I'm
wrong, I have to guess since you don't provide your reasoning about that
example, it seems that you think one should be able to call a stdcall printf by
employing cdecl calling convention at the call site?


> Quite obviously, it can't.

The error is at the second word, the "obviously". ;-)

See above for details.

Alexander Grigoriev

unread,
Jul 20, 2008, 11:42:50 PM7/20/08
to

"Liviu" <lab...@gmail.c0m> wrote in message
news:u3R2eDt6...@TK2MSFTNGP05.phx.gbl...

>
> That was to sink the point that your calling convention couldn't
> implement a compliant printf while maintaining stdcall compatibility.
> Quite obviously, it can't.
>
> Cheers,
> Liviu
>

This fruitful discussion is becoming so exciting that I just can't wait till
Godwin's law takes its turn.

Igor Tandetnik

unread,
Jul 21, 2008, 1:37:33 AM7/21/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:Ta-dnXgco-Stfh7V...@posted.comnet

> This is going far out in details about matters not related to the
> original error you made, when stating that stdcall can't support
> variadic functions.

Or the error you made when you declared the calling convention you
invented to be the same as existing stdcall.

> I'm trying to answer as best I can, but I think if you continue this
> you will sooner or later find something unrelated to original issue
> that I don't know (I don't know all).
>
> So what have you then achieved?

I seem to have misplaced my crystal ball. Without it, it's hard to know
what I will or will not achieve at some undetermined point in the
future.

>>> Now let's consider stdcall with variable number of arguments and a
>>> function that doesn't infer its other arguments from some known
>>> argument(s). In that case, the requirements of stdcall /dictate/
>>> that somehow the argument stack area is passed: it is a direct
>>> logical consequence of documented stdcall requirements.
>>
>> Well, documented stdcall requirements state that it can't be used for
>> variadic functions in the first place, but I'll let it slide.
>
> No you shouldn't let that slide.

And who gave you the authority to tell me what I should or shouldn't let
slide?

> I'm not familiar with any such
> documented requirements. There is documentation of the Visual C++
> "__stdcall" keyword, that I read as that it doesn't implement stdcall
> but instead cdecl calling convention when applied to variadic
> function.

Right. So therefore, stdcall is not supported for variadic functions.

>> I'm assuming you are talking about your modified stdcall.
>
> I'm talking about stdcall calling convention, implemented any way
> that works. :-)

That's just a word game.

>>> As a simple first example, consider then
>>>
>>> void bar( ... );
>>> void foo( ... ) { bar( someNotationForPassingOriginalArgs ); }
>>>
>>> which includes the case of a recursive foo reusing args, and the
>>> special case of a tail recursive foo reusing args.
>>
>> I'm not sure I follow. Tail recursion is usually eliminated by simply
>> jmp-ing to the beginning of the function, not by mucking with stack
>> frames. That would work equally well in stdcall or cdecl function.
>> Basically, a tail recursion is rewritten as a loop - surely both
>> stdcall and cdecl functions can run loops.
>
> Yes, both can handle tail recursion. The point of mentioning that was
> just that also stdcall can handle tail recursion efficiently in this
> particular case. It seems I must stop mentioning details in advance.

And cdecl can only handle recursion less efficiently in this particular
case?

>> And I don't see how non-tail recursion could reuse arguments. After
>> all, the original call would need to preserve some state in order to
>> continue with its work after the recursive call. How would it do
>> that, while allowing the recursive call to trample on its stack
>> frame?
>
> With stdcall the function knows the size of the argument area on the
> stack.
> All it needs to do is to either copy that area or reuse it, depending
> on what state on the stack (local variables below) it needs to
> preserve or not.

Ah. I misunderstood what you meant by "reusing arguments". I thought you
meant reusing them in-place, so that the current call gives up its
allocated stack frame to a recursive call. Which would be pretty much
the same as jmp-ing to the beginning (of the same or different
function), suitable only for tail recursion or tail call.

What you seem to be proposing is a new feature in the C and/or C++
language, whereby one variadic function can call another and pass to it
the exact same set of arguments it itself received, by copying them over
down-stack, below its own locals. You sure weren't kidding when you said
this is going far out.

I guess I even know what someNotationForPassingOriginalArgs might look
like:

void bar(va_list vl);

Same effect, no need to copy anything, works fine with cdecl.

>> What is this someNotationForPassingOriginalArgs you are talking
>> about? Could you elaborate? Perhaps with an illustrative assembly
>> sequence?
>
> Current C or C++ do not have notation for argument forwarding.

Correct. The va_list parameter seems to do the job.

> C++0x
> will have such notation, specific syntax for argument forwarding, but
> I'm not familiar with it (IIRC there is a g++ implementation), and I
> don't know whether it would applicable here, although probably it
> would be.

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

This solves an entirely different problem, having nothing to do with
variadic functions and everything to do with generic programming.

>> I concede: your modified stdcall can catch some, but not all,
>> misuses of printf.
>
> Good, except it's not modified. There's no modification of stdcall
> requirements.

Word games. We'll just have to agree to disagree here.

>> That is incorrect. First, this page states, and I quote: "the
>> compiler makes vararg functions __cdecl".
>
> That "incorrect" is an invalid inference.

This "is not" - "is too" game gets tiresome. I suggest we stop it. All
the arguments either way have been exhausted by now, and are obviously
unconvincing to the opposite side. No new arguments seem to be
forthcoming. A reasonable reader should be able to form his or her own
optinion.

> Whether
>
> A) You regard a variadic function declared __stdcall as having
> stdcall calling convention.

I can't declare a variadic function __stdcall. Or rather, I can, but it
will use __cdecl calling convention at assembly level. It would be
pretty silly to claim that a function using __cdecl calling convention
is in fact __stdcall.

In other words, if I write

void __stdcall f(int x, ...);

or

void __cdecl f(int x, ...);

the exact same machine code will be generated by VC compiler - one
matching __cdecl calling convention. So function f() is __cdecl, just
misleadingly labeled.

http://www.geocities.com/uniart/mix/kp.htm
If upon a cage of an elephant you will see a sign reading: "buffalo", do
not believe your eyes.

The phrase loses much in translation, unfortunately.

> B) You regard a variadic function declared __stdcall as not having
> stdcall calling convention, but cdecl calling convention.

I choose the B pill.

> In this case your quote is completely irrelevant to what's done
> in the stdcall convention.

This is incorrect. The quote is relevant in that it shows that there
ain't no such thing as __stdcall calling convention for variadic
functions. When applied to a variadic function, __stdcall keyword
becomes synonymous with __cdecl.

This is not entirely unprecedented. Keywords "class" and "typename" are
synonymous and interchangeable in template parameter list, but are
distinct elsewhere.

>> Second, it doesn't document that the
>> compiler is allowed, even in some cases, to place additional
>> information on the stack beyond arguments themselves.
>
> First, I think you mean "pass additional information", since a
> requirement to pass that information on the stack would be stupid.

The example you showed at the very beginning did pass additional
information on the stack, not on registers. Was it stupid?

> Now, if this argument was valid (which it isn't) then RVO
> optimization would be prohibited for stdcall functions, as it does
> pass additional information.

What additional information does RVO pass? As far as I can tell, it's
done without any help or knowledge of the caller.

> For example, with
>
> struct Foo{ int x; Foo(): x(42) {} };
>
> Foo __stdcall blah() { Foo o; return o; }
>
> int main()
> {
> Foo o = blah();
> std::cout << o.x << std::endl;
> }
>
> in an ordinary debug build the Visual C++ compiler (which is what the
> documentation refers to) adds a hidden argument, the address of 'o',
> in register eax.

This is incorrect. The hidden argument is pushed on the stack. This is
the assembly generated by the call (debug build, VC 7.1):

lea eax,[o]
push eax
call blah

And the last instruction of blah() is "ret 4" (to pop this hidden
argument off the stack).

> It does that even if blah() is just declared and defined in another
> file.

Yes, when a function returns a class (a non-POD structure or a POD
structure that doesn't fit into EDX:EAX), the compiler passes an
additional argument (the first, leftmost, one). It is a pointer to an
uninitialized buffer (usually allocated on the caller's stack) large
enough to hold the instance. The callee constructs the return value into
this buffer. This works the same for all calling conventions, and is
(rather poorly) documented here:

http://msdn.microsoft.com/en-us/library/984x0h58.aspx

However, I don't understand what any of this has to do with return-value
optimization (RVO). RVO is purely a callee's implementation detail. In
your example, this would be the difference between

// No RVO
void blah(void* returnGoesHere) {
Foo o;
new(returnGoesHere) Foo(o);
}

and

// With RVO
void blah(void* returnGoesHere) {
// Temporary on the stack is elided, instance constructed
// directly into caller-provided buffer.
new(returnGoesHere) Foo();
}

The details don't change in the slightest whether blah is stdcall or
cdecl.

>> Any such leeway would have to
>> be documented so that various tools could agree on precise stack
>> layout (which is, after all, the purpose of a calling convention).
>
> Hm, that's a mixture of good and bad in same sentence.
>
> Let's take the bad first. _stdcall is a calling convention that
> applies to e.g. functions like blah() above. I hope you don't
> disagree with that.

Of course not. It's a fixed-signature function marked __stdcall.

> When such a function has arguments or result of a type that can vary
> between C++ compilers or with various options even on given OS, then
> it cannot in general be called, without adding in low-level
> shenanigans, from source code compiled with any other compiler

Correct. Various tools have to agree on several things, calling
convention being just one of them.

> with incompatible options. Thus a calling convention only supplies
> interoperability to the degree that languages and their
> implementations already allow that interoperability. And in
> particular, it does not impose a precise stack layout, for if it did
> then it would, e.g., exclude most of C++.

Calling convention does specify precise stack layout, once other things
are also agreed upon. How else would you be able to write functions in
assembly and consume them in C++?

> So the "Any such leeway" is invalid (take a look at above RVO code
> again).

You keep using this word, but it doesn't mean what you think it means
(unless, of course, you are about to redefine it from its conventional
meaning to suit your argument). RVO has nothing, I repeat nothing, to do
with calling conventions, and is transparent to the caller.

> On the other hand, in order to adopt such a technique it would be
> most practical if the OS vendor, Microsoft, did document their
> version.

They did already. I guess they are happy with the way it is.

> They could even call it a new calling convention, whatever.

I suspect before they woud consider doing that, it would have to be
demonstrated that it would benefit them, and/or their customers,
sufficiently enough to be worth the effort. I don't think this burden
has been met yet.

> On the third hand, this is talking about hypotheticals, and really
> assumes that stdcall can handle variadic functions (which it can, as
> demonstrated).

You mean, something you insist on calling stdcall but isn't, can handle
variadic functions. Sorry, couldn't resist this one last time.

>>> However, the technique is probably not inferior to cdecl.
>>
>> Well, it is in at least some respects. The call site is same size,
>> but the callee's epilogue is more involved for modified stdcall.
>>
>> Perhaps there are other aspects where modified stdcall is better than
>> cdecl, but I have yet to see a convincing example of those (besides
>> printf guarding against stack overrun, which in my personal opinion
>> is not worth the bother: yours of course may differ).
>
> That's turning things on their head. First you're denying that
> stdcall can handle variadic functions, but being taken up on that,
> now you require me to /convince/ you that it's more efficient. Bah.

Your misunderstanding stems from conflating traditional and modified
stdcalls - claiming that they are one and the same. While I am careful
to distinghuish between the two.

Stdcall can't handle variadic functions. Your modified stdcall (or
whatever you want to call it) can. I state that it does so less
efficiently than cdecl does, and so there's no reason for tool authors
to add modified stdcall to their implementation.

>>> Since cdecl
>>> can always be used as an alternative except for e.g. the two cases
>>> discussed above, which anyway aren't supported today, what matters
>>> in practice is speed and size, where only measurements can tell, and
>>> then perhaps not even in general but just for specific applications
>>> and contexts. I think it would *probably* come out the winner on
>>> both counts.
>>
>> Since the call site is same size, and the function body is strictly
>> larger and more complicated, I don't see under what set of
>> circumstances modified stdcall can ever win such a benchmark.
>> Definitely not by size, which is predictable. Could you explain how
>> modified stdcall can be faster than cdecl, even theoretically? What
>> combination of caching, branch prediction or other arcane factors
>> could possibly help it out?
>
> Simply by having available more information it permits optimizations
> to be made, because optimizations are in the end only ways to exploit
> available information

So far, the only optimization you showed affects a language feature that
doesn't even exist. First you introduce a pessimization - the function
has to copy over its argument list - and then say that an optimizing
compiler may, in some cases, perhaps optimize this copy away. All the
while, there's an existing mechanism that achieves the same goal and
doesn't require any copy (witness vprintf). Color me unimpressed.

>> I
>> suspect (but don't have any proof, before you ask) that existing CPUs
>> are carefully optimized for existing calling conventions, rather than
>> hypothetical ones.
>
> Well I don't know and I don't care about the efficency; as I wrote
> above only measurements can tell. If my speculation on that turns out
> to be wrong, or right, so what? Why are you interested in
> micro-efficiency for a variadic function, typically ineffecient
> anyway?

I'm not. I'm perfectly OK with variadic functions continuing to use
cdecl, and fixed-signature functions continuing to use stdcall (or
cdecl, for that matter).

I'm only saying that if you come up with a new idea, and suggest other
people do work based on this idea (as in, for Microsoft and perhaps
other compiler vendors to support modified stdcall in their tools), then
it seems to be incumbent on you to demonstrate that the idea produces
some improvement over existing state of affairs. So far, I have seen
some downside, however minor, and not much in the way of an upside -
probably not enough to tip the scales towards "let's do work" decision
(the scales, as usual, start heavily skewed towards "don't do work").

>>> Your
>>> characterization as "inferior" is however not backed up by any
>>> argument
>>
>> That is incorrect. I have mentioned it many times before: the call
>> site is same size, but the function epilogue is larger with modified
>> stdcall than it is with cdecl.
>
> Well that's not something anyone objective and competent would use to
> measure superiority or inferiority by.

Why? It makes my compiled program larger - that sounds like "inferior"
to me. What do I get in return?

> Anyway what does inferiority
> or superiority in /any/ respect have to do with your error?

What error? I don't recall making an error.

>>>>> Please provide a link to such unexpanded definition.
>>>> http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx
>>>> http://msdn.microsoft.com/en-us/library/a5s9345t.aspx
>>>> http://msdn.microsoft.com/en-us/library/25687bhx.aspx
>>>>
>>>> On the last page, note the diagram of the stack frame for __stdcall
>>>> function. No evidence of total size, or any other additional
>>>> information beyond function arguments themselves.
>>> He he, ROTFL. Have you consider a register?
>>
>> Yes I have. The calling convention documents the use of registers,
>> too. Consider __thiscall and __fastcall, shown on this same page.
>
> He he. Nothing there about registers in general, or EAX for RVO

Oh no, here comes RVO again. There is no special assignment of EAX
register in stdcall, with RVO or otherwise.

> But I looketh, and somehow ended up on <url:
> http://msdn.microsoft.com/en-us/library/f9t8842e(VS.71).aspx>,
> documenting an [.exe]'s entry point, as specified by linker /ENTRY,
> to have WinMain signature...
> You know, this documentation isn't worth anything: it's not quality.

I'm not sure what you find objectionable about that page specifically -
it looks OK to me - but I would agree that MSDN documentation leaves
something to be desired. "Isn't worth anything" is a hyperbole of
course - it's worth a lot to me, as without it I wouldn't be able to do
my job, and thus wouldn't be able to collect my salary. Nothing perfect
under the sun, make do with what you have, and all that.

> It's what we have to relate to, though, and for that you have to add
> simple sound judgement, not take the incompetent tech-writer's word
> as gold, or, in particular, as you do, think that's something not
> mentioned is forbidden.

Well, if I write, say, a function in assebmly that is supposed to be
consumable from a C program using stdcall calling convention, and the
compiler springs a surprise on me by passing some additional information
and expecting me to act on it (you say it can do pretty much anything,
whether mentioned in the documentation or not), what do I do? What
documentation do I turn to to figure out what's just happened?

That wouldn't be much of a calling convention, now would it?

You can't really expect a specification to list exhaustively everything
a compiler _cannot_ do? There are infinitely many things it could
possibly do - do they all have to be explicitly prohibited? Instead, the
specification documents what the compiler does, and the implication is
that I can rely on it not doing any more (or at least, not doing
anything that affects me or requires my intervention).

>>> Not that concrete
>>> examples that do not illustrate the relevant context, are relevant
>>> in any way (disclaimer: I haven't looked at these examples,
>>> confident that they're not relevant).
>>
>> Well, given that we are talking about variadic functions, and that
>> the __stdcall documentation (the first cited page) says ""the
>> compiler makes vararg functions __cdecl", it's hard to expect an
>> example of something that's explicitly not supported.
>
> By Visual C++.
>
> As mentioned, it's just a tool issue.

Well, these are the tools I get to play with. When your modified stdcall
is supported by vendors, I'll be happy to return to this topic and
discuss these implementations.

>>>>> It is not at all difficult to /design/ foo so that e.g. first
>>>>> argument says how many arguments follow. Nor is it difficult to
>>>>> provide foo with a library routine it can call in order to get
>>>>> correct automatic stack cleanup prior to returning.
>>>> That's precisely the same as the compiler directive I said you
>>>> would need, the one you ridicule below.
>>> Sorry, then I misunderstood you.
>>
>> I wonder if, in the future, before calling people stupid, you might
>> stop and consider that perhaps you have misunderstood something they
>> said. As you see, it's a possiblity.
>
> I wonder if in the future you could refrain from such insinuations?

Which insinuations? The one that you might misunderstand someone? Didn't
you just misunderstand me?

Or the one that you might call someone stupid? Didn't you call me that?

> I strive to only tell people they're being stupid when they're
> actually are being stupid

Is there an objective test for when a person should be properly called
stupid? Or is it just your considered and measured opinion that matters
in the issue? If the latter, is it ever possible for this opinion to be
wrong? Or in this particular area, you are perfect and never make
mistakes of judgement? Or perhaps someone appointed you to make a
determination of whether a particular person is stupid or not, and so
whoever you call stupid is stupid by definition?

> and being told might help them: as I
> recall, in this case that didn't apply.

This "usenet" you speak of must be a truly marvellous place. I bet an
argument like "you are stupid" does wonders to promote a reasonable
technical discussion (assuming this is what you are actually interested
in over there).

In our quiet little neck of the woods you call "Microsoft world", we
prefer to maintain a civil discourse. To our uneducated, unwashed
selves, this way appears more productive. But what do we know, compared
to the great and wise old-timers?

> We were discussing you error in stating that stdcall can't support
> variadic functions.

And it still can't.

>>>> I guess it is possible
>>>> for the caller to prepare and pass a complete description of actual
>>>> argument types, and for va_arg to verify that it's used in
>>>> accordance with this description. That could be a valuable
>>>> debugging aid, but the overhead would probably be too high for
>>>> production code.
>>> Same as with MS's "safe" string functions.
>>
>> Well, those don't quite go to _these_ lengths. They just take a
>> buffer size along with the buffer pointer: error checking is
>> straightforward. Many people consider them plenty fast for
>> production code. Windows OS source code itself reportedly uses them:
>>
>> http://msdn.microsoft.com/en-us/library/ms995349.aspx
>> http://download.microsoft.com/download/8/6/5/8659f5ec-6eaa-4b1f-9107-3e1ec9edf39c/secure_platform.doc
>>
>> (search for "string handling"). At the very least, Microsoft is
>> definitely pushing them for use in production code, not just for
>> debugging.
>
> How much less do you think passing a buffer length, as opposed to
> stack area size, is? When you write "go to _these_ length". I just
> wonder.

I was not constrasting safe string functions with passing just the
argument size, but with passing a complete description of arguments so
that variadic functions like printf could be fully checked (we were
talking about safety, remember). You set up a strawman, and then
proceeded to soundly defeat it. Good job.

>>>> Are there other scenarios where cdecl is less safe than stdcall
>>>> (whether "traditional" or "expanded")?
>>> Don't know. Please don't make me think. :-)
>>
>> Ah. So you state a claim, but decline to back it with any argument.
>> Isn't that the same sin you often accuse me of?
>
> I'm pretty sure that's an attempt at insinuation, but if you would
> quote the claim you think has been made then this can be discussed.

"Your characterization as "inferior" is however not backed up by any
argument"

>> Hmm, I wonder what one might call a person who refuses to think.


>> Perhaps one or more of the terms you used to describe me might fit?
>
> I'm refusing to do your thinking for you.

I have my opinion, but I'll keep it to myself (except to suggest that
you might want to look up "sarcasm" in the dictionary).

Norbert Unterberg

unread,
Jul 21, 2008, 2:10:47 AM7/21/08
to

Alf P. Steinbach schrieb:

> * Igor Tandetnik:
>> "Alf P. Steinbach" <al...@start.no> wrote in message
>> news:1rednZeHX8qKDB7V...@posted.comnet
>>> * Igor Tandetnik:
>>>> I only stated that stdcall, *as it exists today*, cannot support
>>>> variadic functions.
>>> I think what you mean is not that /stdcall/ /cannot/.
>>>
>>> I think what you mean is /Visual C++/ /does not/.
>>
>> My position, of course, is that what Visual C++ does and documents is,
>> by definition, stdcall. Any modification of that is something else. We
>> have already discussed this at length.
>>
>>> Anwyay, the latter is correct, and the former is just nonsense.
>>
>> Now here's a strong, well-reasoned argument. It's so much easier to
>> dismiss an inconvenient point as "nonsense" than to rebut it.
>
> My first posting here in response to you was complete rebuttal in the
> form of working code, where a function called as stdcall convention was
> variadic.
>
> You're denying a matter of fact.

I am following this discussion, but I am slowly losing track about what you are
arguing about. It still seems to deal with different opinions about the term
"stdcall calling convention".

Alf, do you have any link about a formal definition about "stdcall" that
supports your opinion? The best I found was the info on MSDN:

http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx

which claims:

1. Argument-passing order: Right to left.
2. Argument-passing convention: By value, unless a pointer or reference type is
passed.
3. Stack-maintenance responsibility: Called function pops its own arguments from
the stack.
4. Name-decoration convention: An underscore (_) is prefixed to the name. The
name is followed by the at sign (@) followed by the number of bytes (in decimal)
in the argument list. Therefore, the function declared as int func( int a,
double b ) is decorated as follows: _func@12
5. Case-translation convention: None

A calling convention definition should be comlpete in a sense that it lists all
informatino relevant for implementing code that follows this specific
convention. Since in #1 Microsoft only talks about "arguments" I am qute sure
they mean function arguments, and no aditional hidden compiler generated data
(like number of bytes pushed on the stack).

According to #4 the number of bytes in the argument list must be appended to the
function name. This must obvious be true for both the caller and the callee
otherwise the linker would not find a match. That also means that the number of
bytes must be known and fixed when compiling the called function.

How would you implement your variadic stdcall functions without violating one of
these five rules? Or if you think MS is wrong here, do you know a __stdcall
specification which is better or more precise that that of MSDN?

Norbert

Igor Tandetnik

unread,
Jul 21, 2008, 2:09:47 AM7/21/08
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:KKqdnf1E0Ia5cB7V...@posted.comnet

>>> I think what you mean is not that /stdcall/ /cannot/.
>>>
>>> I think what you mean is /Visual C++/ /does not/.
>>
>> My position, of course, is that what Visual C++ does and documents
>> is, by definition, stdcall. Any modification of that is something
>> else. We have already discussed this at length.
>>
>>> Anwyay, the latter is correct, and the former is just nonsense.
>>
>> Now here's a strong, well-reasoned argument. It's so much easier to
>> dismiss an inconvenient point as "nonsense" than to rebut it.
>
> My first posting here in response to you was complete rebuttal in the
> form of working code, where a function called as stdcall convention
> was variadic.

It is curious that you claim that your knurre function is stdcall on
purely syntactical grounds: it is marked as __stdcall, therefore it
obviously is. However, the ... notation didn't appear anywhere in the
source, but you still claim the function is variadic. It didn't look
very variadic to the caller - e.g. it didn't work when called with 10
arguments, or when called with extraneous arguments.

> You're denying a matter of fact.

That is incorrect.

Anyway, I think I'm done with this thread. No new technical arguments
are being advanced, and nobody's opinions are being changed (are they
ever?) Let the dispassionate reader form their own. I'm not interested
in a discussion over an approptiate vs inappropriate use of name-calling
(I have my opinion on that, too, and it's highly unlikely to change).

Apologies rendered, return apologies accepted. I'm happy to let you have
the last word, if you are so inclined. Have a nice ... uh, whatever time
of day you have where you are; it's well past midnight in my part of the
world.

Alf P. Steinbach

unread,
Jul 21, 2008, 3:19:13 AM7/21/08
to
* Igor Tandetnik:

> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:Ta-dnXgco-Stfh7V...@posted.comnet
>> This is going far out in details about matters not related to the
>> original error you made, when stating that stdcall can't support
>> variadic functions.
>
> Or the error you made when you declared the calling convention you
> invented to be the same as existing stdcall.

I have not invented a calling convention (or to be precise, I haven't done that
or referred to that in this thread). Nor have I claimed to invent a calling
convention here. Since I haven't invented a calling convention here, I have
certainly not declared it to be the same as stdcall.

On the contrary, I have posted some working code that uses stdcall calling
convention.

Said code successfully calling a variadic function, using stdcall convention.


>> I'm trying to answer as best I can, but I think if you continue this
>> you will sooner or later find something unrelated to original issue
>> that I don't know (I don't know all).
>>
>> So what have you then achieved?
>
> I seem to have misplaced my crystal ball. Without it, it's hard to know
> what I will or will not achieve at some undetermined point in the
> future.

Seems like you're extremely, extremely afraid of admitting a simple error.


>>>> Now let's consider stdcall with variable number of arguments and a
>>>> function that doesn't infer its other arguments from some known
>>>> argument(s). In that case, the requirements of stdcall /dictate/
>>>> that somehow the argument stack area is passed: it is a direct
>>>> logical consequence of documented stdcall requirements.
>>> Well, documented stdcall requirements state that it can't be used for
>>> variadic functions in the first place, but I'll let it slide.
>> No you shouldn't let that slide.
>
> And who gave you the authority to tell me what I should or shouldn't let
> slide?

What kind of question is that?

When you make statements in a public debate you should be prepared for
respondents telling you about your mistakes.

In a sense, by debating publicly you're saying to anyone who'd care to join that
they're free to criticize your statements and inform you of errors, and I'm part
of that public -- especially since you're debating with me.

This should not be so hard to understand.

Authority arguments and whatnot are simply fallacies, and you should not use
them if you want to be taken seriously.


>> I'm not familiar with any such
>> documented requirements. There is documentation of the Visual C++
>> "__stdcall" keyword, that I read as that it doesn't implement stdcall
>> but instead cdecl calling convention when applied to variadic
>> function.
>
> Right. So therefore, stdcall is not supported for variadic functions.

If you mean, it's not currently supported by Visual C++, then that is correct,
and meaningless, because a calling convention is not defined by what Visual C++
supports (it's a bit more general than that).

If you mean, it's not supported by any tool, then that may or may not be
correct, but is meaningless because you can't know that.

Anyway it's meaningless.


>>> I'm assuming you are talking about your modified stdcall.
>> I'm talking about stdcall calling convention, implemented any way
>> that works. :-)
>
> That's just a word game.

I think I (perhaps mistakenly) a short time ago woved to stop using possibly
offensive correct labels for such statements.

But I'm sure you can think of an appropriate correct label for something so
completely and utterly meaningless, a contemptuous brushing aside of an honest
effort to answer your question completely and technically.

The answer is true, and as precise as possible, but evidently not to your taste.


>>>> As a simple first example, consider then
>>>>
>>>> void bar( ... );
>>>> void foo( ... ) { bar( someNotationForPassingOriginalArgs ); }
>>>>
>>>> which includes the case of a recursive foo reusing args, and the
>>>> special case of a tail recursive foo reusing args.
>>> I'm not sure I follow. Tail recursion is usually eliminated by simply
>>> jmp-ing to the beginning of the function, not by mucking with stack
>>> frames. That would work equally well in stdcall or cdecl function.
>>> Basically, a tail recursion is rewritten as a loop - surely both
>>> stdcall and cdecl functions can run loops.
>> Yes, both can handle tail recursion. The point of mentioning that was
>> just that also stdcall can handle tail recursion efficiently in this
>> particular case. It seems I must stop mentioning details in advance.
>
> And cdecl can only handle recursion less efficiently in this particular
> case?

I don't understand that question, which probably means it's meaningless or
designed to deceive.


>>> And I don't see how non-tail recursion could reuse arguments. After
>>> all, the original call would need to preserve some state in order to
>>> continue with its work after the recursive call. How would it do
>>> that, while allowing the recursive call to trample on its stack
>>> frame?
>> With stdcall the function knows the size of the argument area on the
>> stack.
>> All it needs to do is to either copy that area or reuse it, depending
>> on what state on the stack (local variables below) it needs to
>> preserve or not.
>
> Ah. I misunderstood what you meant by "reusing arguments". I thought you
> meant reusing them in-place, so that the current call gives up its
> allocated stack frame to a recursive call. Which would be pretty much
> the same as jmp-ing to the beginning (of the same or different
> function), suitable only for tail recursion or tail call.
>
> What you seem to be proposing is a new feature in the C and/or C++
> language, whereby one variadic function can call another and pass to it
> the exact same set of arguments it itself received, by copying them over
> down-stack, below its own locals.

No, that fanciful description of my intentions is either designed to deceive, or
a description based on complete lack of understanding.

I hope it is the latter, but I'm pretty sure it is the former, because it takes
some thinking to come up with such.


> You sure weren't kidding when you said
> this is going far out.
>
> I guess I even know what someNotationForPassingOriginalArgs might look
> like:
>
> void bar(va_list vl);
>
> Same effect, no need to copy anything, works fine with cdecl.

No, that is incorrect, in at least two ways.

First, if you don't have a named first argument you can't obtain a va_list.

Second, if the bar function is not yours to control, you can't ensure it will
take a va_list as argument. And if it is yours to control, then there's not much
point in calling it via an intermediary function.


>>> What is this someNotationForPassingOriginalArgs you are talking
>>> about? Could you elaborate? Perhaps with an illustrative assembly
>>> sequence?
>> Current C or C++ do not have notation for argument forwarding.
>
> Correct. The va_list parameter seems to do the job.

No, it doesn't (see above).


>> C++0x
>> will have such notation, specific syntax for argument forwarding, but
>> I'm not familiar with it (IIRC there is a g++ implementation), and I
>> don't know whether it would applicable here, although probably it
>> would be.
>
> http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
> http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm
>
> This solves an entirely different problem, having nothing to do with
> variadic functions and everything to do with generic programming.

Yes and no. It has much to do with variadic functions, although not the kind
we're discussing, but it seems the notation is not applicable here after all.


>>> I concede: your modified stdcall can catch some, but not all,
>>> misuses of printf.
>> Good, except it's not modified. There's no modification of stdcall
>> requirements.
>
> Word games. We'll just have to agree to disagree here.

In technical matters you need to be precise. Modification means change, and no
aspect of stdcall is changed. Mostly I think this is best described as an(y)
/implementation/ of stdcall for variadic functions.


>>> That is incorrect. First, this page states, and I quote: "the
>>> compiler makes vararg functions __cdecl".
>> That "incorrect" is an invalid inference.
>
> This "is not" - "is too" game gets tiresome. I suggest we stop it. All
> the arguments either way have been exhausted by now, and are obviously
> unconvincing to the opposite side. No new arguments seem to be
> forthcoming. A reasonable reader should be able to form his or her own
> optinion.

Well I completely lost the context of what you're referring to now, but anyway,
in my first response to you in this thread I told you that it's a matter of tool
support, and you have found quotes to prove that wrt. Visual C++ -- but you
insist that those quotes say the opposite. Oh well.

In Visual C++, yes __stdcall is replaced with __cdecl for variadic function

In other words, Visual C++ doesn't currently support stdcall for variadic functions.

Thus any quote about Visual C++ is irrelevant to what is or is not stdcall
convention for a variadic function, because there's no such thing in Visual C++.


> This is not entirely unprecedented. Keywords "class" and "typename" are
> synonymous and interchangeable in template parameter list, but are
> distinct elsewhere.


They're not quite interchangable in template parameter lists. When you have a
template template parameter you need to use 'class', not 'typename'.


>>> Second, it doesn't document that the
>>> compiler is allowed, even in some cases, to place additional
>>> information on the stack beyond arguments themselves.
>> First, I think you mean "pass additional information", since a
>> requirement to pass that information on the stack would be stupid.
>
> The example you showed at the very beginning did pass additional
> information on the stack, not on registers. Was it stupid?

Uh, <teaspoon mode>.

A requirement is different from an example.

What's stupid for a requirement need not be stupid for an example.

For example, a red car isn't stupid as an example of a car; a requirement that
all cars must be red would be stupid.

</teaspoon mode>


>> Now, if this argument was valid (which it isn't) then RVO
>> optimization would be prohibited for stdcall functions, as it does
>> pass additional information.
>
> What additional information does RVO pass? As far as I can tell, it's
> done without any help or knowledge of the caller.

RVO passes a pointer to the result object storage.


>> For example, with
>>
>> struct Foo{ int x; Foo(): x(42) {} };
>>
>> Foo __stdcall blah() { Foo o; return o; }
>>
>> int main()
>> {
>> Foo o = blah();
>> std::cout << o.x << std::endl;
>> }
>>
>> in an ordinary debug build the Visual C++ compiler (which is what the
>> documentation refers to) adds a hidden argument, the address of 'o',
>> in register eax.
>
> This is incorrect. The hidden argument is pushed on the stack. This is
> the assembly generated by the call (debug build, VC 7.1):
>
> lea eax,[o]
> push eax
> call blah
>
> And the last instruction of blah() is "ret 4" (to pop this hidden
> argument off the stack).

Dang, you got me there. Passed on the stack and not in register eax. Oh dear!

Anyways, regarding your question "What additional information", that's this
pointer you have here.

I gather from this + earlier comments that you don't really understand the
assembly code level.


>> It does that even if blah() is just declared and defined in another
>> file.
>
> Yes, when a function returns a class (a non-POD structure or a POD
> structure that doesn't fit into EDX:EAX), the compiler passes an
> additional argument (the first, leftmost, one). It is a pointer to an
> uninitialized buffer (usually allocated on the caller's stack) large
> enough to hold the instance. The callee constructs the return value into
> this buffer. This works the same for all calling conventions, and is
> (rather poorly) documented here:
>
> http://msdn.microsoft.com/en-us/library/984x0h58.aspx

I can't find any documentation of it there, so possibly wrong URL.

But anyway, you didn't understand this completely enough to avoid asking "What
additional information" above.

Are you by any chance George? Or, really, is George you?


> However, I don't understand what any of this has to do with return-value
> optimization (RVO). RVO is purely a callee's implementation detail.

No, it certainly isn't. :-)

RVO requires the passing in of a pointer to the caller's storage where the
result object should be created.

This has to be done by every caller, and that's why a pointer is passed in.


> In
> your example, this would be the difference between
>
> // No RVO
> void blah(void* returnGoesHere) {
> Foo o;
> new(returnGoesHere) Foo(o);
> }
>
> and
>
> // With RVO
> void blah(void* returnGoesHere) {
> // Temporary on the stack is elided, instance constructed
> // directly into caller-provided buffer.
> new(returnGoesHere) Foo();
> }
>
> The details don't change in the slightest whether blah is stdcall or
> cdecl.

No, but hey, are you sure you're not George?

Anyways, (OK this may be different to grok if you don't understand the pointer
argument of RVO, but)

with RVO you have a kind of routine, namely function returning class type
result, that with stdcall and possibly other calling conventions requires each
caller to supply a hidden argument, namely pointer to caller's storage for
result, and

with variadic function you have a kind of routine, namely variadic routine, that
with stdcall and possibly other calling conventions requires each caller to
supply a hidden argument, namely the number of argument bytes.

Note how under stdcall these two kinds of routines impose the same kind of
requirement on callers, that a hidden argument must be passed, and that that's
very compiler (and even option!) dependent.

For one of these kinds of routines you have argued that that hidden argument had
to be part of the stdcall calling convention, that it would not do if two
different compilers or, god help us, compiler option sets, did it differently.

For the other kind, if you're consistent, you'll have to argue the same.

Earlier in the article I'm responding to you asked about what additional
information was passed for the RVO case.

That you don't understand that means you don't understand anything about RVO.


> RVO has nothing, I repeat nothing, to do
> with calling conventions, and is transparent to the caller.

Given that you had to ask, perhaps you should think a little harder about this.

:-)


>> On the other hand, in order to adopt such a technique it would be
>> most practical if the OS vendor, Microsoft, did document their
>> version.
>
> They did already. I guess they are happy with the way it is.

No, I'm sorry, as far as I know that's incorrect again: as far as I know
Microsoft has not adopted such a technique.


>> They could even call it a new calling convention, whatever.
>
> I suspect before they woud consider doing that, it would have to be
> demonstrated that it would benefit them, and/or their customers,
> sufficiently enough to be worth the effort. I don't think this burden
> has been met yet.

Of course not.

I'm sorry but you're making meaningless noise.

>> On the third hand, this is talking about hypotheticals, and really
>> assumes that stdcall can handle variadic functions (which it can, as
>> demonstrated).
>
> You mean, something you insist on calling stdcall but isn't, can handle
> variadic functions. Sorry, couldn't resist this one last time.

What other criterion can one have for stdcall than that all the listed
criterions are fulfilled?


>>>> However, the technique is probably not inferior to cdecl.
>>> Well, it is in at least some respects. The call site is same size,
>>> but the callee's epilogue is more involved for modified stdcall.
>>>
>>> Perhaps there are other aspects where modified stdcall is better than
>>> cdecl, but I have yet to see a convincing example of those (besides
>>> printf guarding against stack overrun, which in my personal opinion
>>> is not worth the bother: yours of course may differ).
>> That's turning things on their head. First you're denying that
>> stdcall can handle variadic functions, but being taken up on that,
>> now you require me to /convince/ you that it's more efficient. Bah.
>
> Your misunderstanding stems from conflating traditional and modified
> stdcalls - claiming that they are one and the same. While I am careful
> to distinghuish between the two.

Sorry, it's not a misunderstanding: the length of this thread is excessive
evidence of the excessive lengths you go to to avoid admitting an error.


> Stdcall can't handle variadic functions.

You can't be that stupid.


>Your modified stdcall (or
> whatever you want to call it) can. I state that it does so less
> efficiently than cdecl does, and so there's no reason for tool authors
> to add modified stdcall to their implementation.

Whatever. It's a valid implementation of stdcall, fulfilling all stdcall
requirements, and callable via stdcall convention, actually called and working.
So regarding your statement above, again, I simply refuse to believe you are so
stupid as to deny that this code fulfills all stdcall criterions.


>>>> Since cdecl
>>>> can always be used as an alternative except for e.g. the two cases
>>>> discussed above, which anyway aren't supported today, what matters
>>>> in practice is speed and size, where only measurements can tell, and
>>>> then perhaps not even in general but just for specific applications
>>>> and contexts. I think it would *probably* come out the winner on
>>>> both counts.
>>> Since the call site is same size, and the function body is strictly
>>> larger and more complicated, I don't see under what set of
>>> circumstances modified stdcall can ever win such a benchmark.
>>> Definitely not by size, which is predictable. Could you explain how
>>> modified stdcall can be faster than cdecl, even theoretically? What
>>> combination of caching, branch prediction or other arcane factors
>>> could possibly help it out?
>> Simply by having available more information it permits optimizations
>> to be made, because optimizations are in the end only ways to exploit
>> available information
>
> So far, the only optimization you showed affects a language feature that
> doesn't even exist. First you introduce a pessimization - the function
> has to copy over its argument list - and then say that an optimizing
> compiler may, in some cases, perhaps optimize this copy away.

You're very confused here, but considering that you don't understand assembly
level that's understandable.

The optimization you're referring to has nothing to do with optimization
permitted by calling convention relative to other calling conventions. It's
purely an internal thing for /additional functionality/ enabled by the calling
convention. When comparing performance to other calling conventions you have to
compare like functionality, not functionality that's only available with one.

> All the
> while, there's an existing mechanism that achieves the same goal and
> doesn't require any copy (witness vprintf).

See above, it isn't and doesn't.

You're seriously confused.


> Color me unimpressed.

It's not surprising that you're not impressed by something that's not meant to
impress.

I resent your insinuation that this technique was presented in order to impress.

It was presented solely as a very hands-on argument that you made an error, and
you're seemingly in denial about that, choosing instead insinuations etc.

>>> I
>>> suspect (but don't have any proof, before you ask) that existing CPUs
>>> are carefully optimized for existing calling conventions, rather than
>>> hypothetical ones.
>> Well I don't know and I don't care about the efficency; as I wrote
>> above only measurements can tell. If my speculation on that turns out
>> to be wrong, or right, so what? Why are you interested in
>> micro-efficiency for a variadic function, typically ineffecient
>> anyway?
>
> I'm not. I'm perfectly OK with variadic functions continuing to use
> cdecl, and fixed-signature functions continuing to use stdcall (or
> cdecl, for that matter).

Good. Then your earlier strong interest was just an infatuation, say. Or
perhaps a very strong desire to learn about basics.


> I'm only saying that if you come up with a new idea, and suggest other
> people do work based on this idea (as in, for Microsoft and perhaps
> other compiler vendors to support modified stdcall in their tools), then
> it seems to be incumbent on you to demonstrate that the idea produces
> some improvement over existing state of affairs. So far, I have seen
> some downside, however minor, and not much in the way of an upside -
> probably not enough to tip the scales towards "let's do work" decision
> (the scales, as usual, start heavily skewed towards "don't do work").
>
>>>> Your
>>>> characterization as "inferior" is however not backed up by any
>>>> argument
>>> That is incorrect. I have mentioned it many times before: the call
>>> site is same size, but the function epilogue is larger with modified
>>> stdcall than it is with cdecl.
>> Well that's not something anyone objective and competent would use to
>> measure superiority or inferiority by.
>
> Why? It makes my compiled program larger - that sounds like "inferior"
> to me. What do I get in return?

I'm not counting bytes in the range of tens or twenty.

It's interesting that you do.


[snipped rest of babble, out of time]

Cheers,

Liviu

unread,
Jul 21, 2008, 3:35:59 AM7/21/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Liviu:
>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>> * Igor Tandetnik:
>>>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>>>
>>>>>>> Hence it's incorrect to say that stdcall cannot or could
>>>>>>> not support variadic number of arguments (as Igor did)
>>>>>>
>>>>>> I only stated that stdcall, *as it exists today*, cannot support
>>>>>> variadic functions.
>>>>>
>>>>> I think what you mean is not that /stdcall/ /cannot/.
>>>>>
>>>>> I think what you mean is /Visual C++/ /does not/.
>>>>
>>>> You seem to rejoice in the misconception that stdcall is a C-only
>>>> thing (and, this one must be new, that VC++ is the only relevant
>>>> compiler).
>>>>
>>>> Any "extension" to the existing stdcall convention which breaks
>>>> binary compatibility is _not_ and can _not_ claim to be stdcall.
>>>
>>> I'm sorry, that's not meaningful.
>>
>> Also, you must appreciate that __stdcall is just the C name for a
>> well defined binary contract between caller and callee. It is not a
>> matter of compiler, and there doesn't even have to be one involved. I
>> could technically write assembler, or direct machine bytecodes in
>> debug.exe if I so wished, and the same calling convention would still
>> apply.
>
> That's correct.

Sigh ;-) n.b. before you ask (again) that's a "sigh of relief".

>>> First, we're not talking about an extension: no constraints of
>>> stdcall are broken, so it /is/, by the definition of stdcall,
>>> stdcall, as evidenced by the calls working just as they should with
>>> stdcall calling convention.
>>
>> Yes, your calls to knurre() do work without breaking any stdcall
>> constraints.
>
> Ah.
>
>> No, the same technique cannot be applied to calling (or writing)
>> variadic functions in general, such as a compliant printf() which
>> doesn't trash its stack when called with excess arguments.
>
> That's demonstrably false.

You had the opportunity to demonstrate it false a few times already,
and declined. Just saying it doesn't count, no matter how many times
you keep repeating yourself.

> The C standard does not impose a calling convention. Hence a C
> compiler is free to use any convention it wants, including stdcall.
>
> And as you noted above the calling convention is not what any
> particular compiler supports.
>
> We're discussing what stdcall can (or "could") support, not,
> hopefully, what Visual C++ supports today.

We're discussing what stdcall can and does support. Not what it "could"
support if one oddly decided to label a new convention as "stdcall".

It's also unclear why you keep bringing visual/c into this, once you
seem to have conceded that the stdcall convention is language and
compiler neutral.

> And as demonstrated all that's necessary is for the compiler to pass
> the number of argument bytes, in some way, when it has a variadic
> function at hand.

Please elaborate that "in some way" part. My point was that it's not
possible without breaking binary compatibility with existing stdcall. Be
welcome to "demonstrate" otherwise, and I mean more than empty words.

> I think it's no good using time on preparing a concrete example for
> you: you could in principle easily do that yourself, based on the code
> I posted. But outline: if you're the compiler, when you have a stdcall
> variadic function at hand, to call, you push the arguments right to
> left, pass the total number of argument bytes in some way, e.g. in a
> register, and then fire and forget.

I guess "e.g. in a register" means reserving a register for that purpose
in your proposed calling convention. There is no such reservation in the
stdcall specs. Any existing code which rightfully assumes that said
register is available to use for its own purposes would break. So much
for binary compatibility.

>>> Second, adopting this technique does not break any binary
>>> compatibility unless it's done by someone incompetent.
>

> "This technique" refers to using the stdcall convention and, for a
> variadic function, if necessary pass the number of argument bytes in
> some way.

To maintain binary compatibility with stdcall, that "in some way" would
have to use no registers, no stack, no other memory. Maybe you know
some trick the rest of us don't, please share ;-)

>> Yet, in case you suggest that the __knurre convention could
>> (a) be extended to cover variadic functions in general, including
>> those like printf which must tolerate excess arguments, and
>
> I apologize if I have only suggested that.
>
> To me it's a trivial consequence.

...to a false premise, see above.

>> Since stdcall defines no such mechanism and reserves no location
>> (hidden param, designated register, stack slot) to accommodate that
>> additional information, any place you'd choose to pass the info would
>> have been up for grabs under the existing stdcall convention,
>
> No, not at all. There are only two cases to consider.
>
> Either (1) the language implementation already supports variadic
> stdcall routines, in which case it necessarily already does this. :-)

If only it could ;-)

> Or, (2), it does not, in which case with the language implemention
> we're considering extending there are no existing calls to variadic
> stdcall functions, no existing such functions, and therefore all at
> the machine code level is free, within the constraints of argument
> pushing order and stack adjustment.

You seem to be fixated on C and strongly typed languages. They are not
the only ones around, you know. And stdcall is not a C-only affair, and
your knurre() is not the first or only variadic function to pop its own
stack. Any code currently using a knurre-like function in a different
module would break if you redefined stdcall and recompiled the library.
So much for nearsightedness.

>>>> Talk is cheap, again. Please go back to that assembler snippet
>>>>
>>> The assembler snippet you posted was a challenge to deduce a
>>> caller's supplied arguments in the absence of any information about
>>> them.
>>
>> That was to sink the point that your calling convention couldn't
>> implement a compliant printf while maintaining stdcall compatibility.
>
> Well, the example was completely irrelevant. It seems, please correct
> me if I'm wrong,

Happy to oblige ;-) You are wrong.

> it seems that you think one should be able to call a stdcall printf by
> employing cdecl calling convention at the call site?

Huh? Can't fathom where you'd get the cdecl idea. No, those are
stdcall's to a hypothetical __stdcall printf you claimed you could
write, but never actually did.

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 21, 2008, 4:08:05 AM7/21/08
to
* Norbert Unterberg:

I think that's as good one as any.

Microsoft's documentation generally mixes Visual C++ with the Windows API (e.g.,
for SEH exceptions, and for calling conventions, and for GDI+).

Which means it can /generally/ be very difficult for us to untangle what's
specific to what: we have to apply sound judgement.


> which claims:
>
> 1. Argument-passing order: Right to left.
> 2. Argument-passing convention: By value, unless a pointer or reference
> type is passed.
> 3. Stack-maintenance responsibility: Called function pops its own
> arguments from the stack.
> 4. Name-decoration convention: An underscore (_) is prefixed to the
> name. The name is followed by the at sign (@) followed by the number of
> bytes (in decimal) in the argument list. Therefore, the function
> declared as int func( int a, double b ) is decorated as follows: _func@12
> 5. Case-translation convention: None
>
> A calling convention definition should be comlpete in a sense that it
> lists all informatino relevant for implementing code that follows this
> specific convention.

I think I agree with what I think you intend to say... :-) But a calling
convention can't cover everything, especially not what it was not designed to
but could have been designed to. For example, the page you refer to above has
examples of __stdcall applied to non-static member functions, but no discussion
of exactly what that means e.g. for this-pointer, which is an omission.


> Since in #1 Microsoft only talks about "arguments"
> I am qute sure they mean function arguments, and no aditional hidden
> compiler generated data (like number of bytes pushed on the stack).

I'm quite sure that for a call of a non-static __stdcall member function, an
additional "this" pointer has to be passed. ;-)

Since it apparently isn't specified, different compilers might do this in
different ways (well, except for COM, which IIRC uses __stdcall for member funcs
(perhaps it is specified as part of COM)).


> According to #4 the number of bytes in the argument list must be
> appended to the function name. This must obvious be true for both the
> caller and the callee otherwise the linker would not find a match. That
> also means that the number of bytes must be known and fixed when
> compiling the called function.

Yes, this means variadic __stdcall functions are not supported by Visual C++.

Interestingly, or not, the documentation's point #4 is incorrect.

It's said to apply to Visual C++, but applies only to C or C++ "extern 'C'",
which AFAICS isn't mentioned on that page: for a not-"extern 'C'" C++ function
the information is incorrect.


> How would you implement your variadic stdcall functions without
> violating one of these five rules? Or if you think MS is wrong here,

See above about error and omission on that page -- it's practically impossible
to find something Microsoft there's nothing obviously wrong with! <g>

No compiler could implement stdcall functions, and make them work according to
C++ rules (strong type checking), by following the rules on that page, so yes,
MS is wrong here. It really takes something to be wrong about one's own
specification. But MS has whatever it takes. :-)

So the question is merely, where should we draw line at applying sound
judgement? Are those rules the final and full story after obvious errors and
omissions corrected? Or is it likely that e.g. the name mangling scheme, when
corrected so that it applies to C++ instead of C, is specific to Visual C++ and
not part of the Windows API stdcall convention?

I'm contending that the name mangling is specific to Visual C++.


> do
> you know a __stdcall specification which is better or more precise that
> that of MSDN?

Unfortunately (see above about errors and omissions) Microsoft is mostly in
charge of their documentation. However, they've been forced by a few court cases
and by European Union to start improving the documentation. Hopefully that may
have some effects also on the documentation they've not been forced to fix/add.

Cheers,

Alf P. Steinbach

unread,
Jul 21, 2008, 4:13:23 AM7/21/08
to

Oh.


>>>> First, we're not talking about an extension: no constraints of
>>>> stdcall are broken, so it /is/, by the definition of stdcall,
>>>> stdcall, as evidenced by the calls working just as they should with
>>>> stdcall calling convention.
>>> Yes, your calls to knurre() do work without breaking any stdcall
>>> constraints.
>> Ah.
>>
>>> No, the same technique cannot be applied to calling (or writing)
>>> variadic functions in general, such as a compliant printf() which
>>> doesn't trash its stack when called with excess arguments.
>> That's demonstrably false.
>
> You had the opportunity to demonstrate it false a few times already,
> and declined. Just saying it doesn't count, no matter how many times
> you keep repeating yourself.

Well, I demonstrated it *again* in the following paragraphs.

Alf P. Steinbach

unread,
Jul 21, 2008, 4:32:16 AM7/21/08
to
* Liviu:

>>
>> We're discussing what stdcall can (or "could") support, not,
>> hopefully, what Visual C++ supports today.
>
> We're discussing what stdcall can and does support. Not what it "could"
> support if one oddly decided to label a new convention as "stdcall".

Let's be clear.

What stdcall does support is (1) what it's documented as supporting, + (2) what
Visual C++ implicitly lets it support (that is mostly stdcall for member
functions, used in COM, but perhaps it's documented separately).

What stdcall can support is something else. Then we are into the "could".
stdcall as a calling convention can support variadic functions, and Visual C++
could therefore support stdcall variadic functions (but doesn't).


> It's also unclear why you keep bringing visual/c into this, once you
> seem to have conceded that the stdcall convention is language and
> compiler neutral.

Unfortunately Microsoft's documentation of some parts of the Windows API is
mixed up with their documentation of Visual C++, and very difficult to separate.


>> And as demonstrated all that's necessary is for the compiler to pass
>> the number of argument bytes, in some way, when it has a variadic
>> function at hand.
>
> Please elaborate that "in some way" part. My point was that it's not
> possible without breaking binary compatibility with existing stdcall. Be
> welcome to "demonstrate" otherwise, and I mean more than empty words.

For example in a register.

Note well that since in our environment there are no existing such routines (if
there are then we would have already have language implementation supporting
stdcall variadic functions), since there are none such, there is absolutely no
way to break binary compatibility with them.

I'm at a loss regarding understanding what it is you don't understand.

But perhaps you're thinking in terms of redefining __stdcall to mean some size
argument must always be passed regardless of routine. Well that would be stupid.
Size argument is only required for variadic routines, which are the ones where
binary compatibility is absolutely not a problem (cause no such binaries).

Please also note, if you're going to raise this question again, then please
include a concrete example that illustrates how you think something can be
broken, because arguing against or trying to explain some unknown hypotetical is
just not constructive.


Cheers,

Ben Voigt [C++ MVP]

unread,
Jul 21, 2008, 10:40:04 AM7/21/08
to
Liviu wrote:
> "Alexander Grigoriev" <al...@earthlink.net> wrote in message
>> news:Otgrjha6...@TK2MSFTNGP03.phx.gbl...
>>
>> "Igor Tandetnik" <itand...@mvps.org> wrote in message
>> news:%23rvqUdV...@TK2MSFTNGP06.phx.gbl...
>>>> Here comes another thing of callng convention: who is destroying
>>>> non-reference non-POD arguments of a function. Such objects are
>>>> destroyed upon function return (according to ISO14882 1.9/13, a
>>>> function call is full expression). These should be destroyed before
>>>> the stack pointer is adjusted back.
>>>
>>> I've just experimented with this:
>>>
>>> void /*__stdcall*/ f(string s);
>>> f("123");
>>>
>>> With both cdecl and stdcall, the calling code is running string
>>> constructor, and the code inside f() is calling ~string destructor.
>>> So it doesn't seem to matter who pops the stack.
>>
>> Yes, it does make sense to destroy those inside the function. That
>> saves code size.
>
> Also makes sense in terms of exception handling, so that he who
> compiles f() doesn't have to rely on caller's mercy (and /E options)
> to dispose of temporaries should an exception actually be thrown.

But this ought to be very interesting in conjunction with variadic
functions... because the arguments must be destructed by the caller.

I believe this requirement in the standard will be the death-blow to Alf's
so-called variadic __stdargs.


Ben Voigt [C++ MVP]

unread,
Jul 21, 2008, 11:04:22 AM7/21/08
to
>> Since cdecl
>> can always be used as an alternative except for e.g. the two cases
>> discussed above, which anyway aren't supported today, what matters in
>> practice is speed and size, where only measurements can tell, and
>> then perhaps not even in general but just for specific applications
>> and contexts. I think it would *probably* come out the winner on both
>> counts.
>
> Since the call site is same size, and the function body is strictly
> larger and more complicated, I don't see under what set of
> circumstances modified stdcall can ever win such a benchmark.
> Definitely not by size, which is predictable. Could you explain how
> modified stdcall can be faster than cdecl, even theoretically? What
> combination of caching, branch prediction or other arcane factors
> could possibly help it out? I suspect (but don't have any proof,
> before you ask) that existing CPUs are carefully optimized for
> existing calling conventions, rather than hypothetical ones.

It shouldn't be too hard to craft an example where knowing the number of
arguments in advance helps performance. For example, a variation on strstr,
where the sought string is passed using varargs instead of by address.
Knowing the length of the sought string would enable word-size comparisons
instead of byte by byte, a potential large performance gain (to such a
degree that I suspect the strstr implementation starts by finding the length
of the sought string).

In fact, it strikes me that there already is a variadic implementation in
the Microsoft C++ compiler that passes the number of arguments: the .NET
calling convention. See
http://msdn.microsoft.com/en-us/library/9dt1588w.aspx


Ben Voigt [C++ MVP]

unread,
Jul 21, 2008, 11:11:06 AM7/21/08
to
>> Why? It makes my compiled program larger - that sounds like
>> "inferior" to me. What do I get in return?
>
> I'm not counting bytes in the range of tens or twenty.
>
> It's interesting that you do.

That's a fairly big chunk of your L1 instruction cache, so maybe you should
be.


Alex Blekhman

unread,
Jul 21, 2008, 12:32:25 PM7/21/08
to
"Ben Voigt [C++ MVP]" wrote:
> But this ought to be very interesting in conjunction with
> variadic functions... because the arguments must be destructed
> by the caller.
>
> I believe this requirement in the standard will be the
> death-blow to Alf's so-called variadic __stdargs.

Actually, it is undefined behaviour to pass anything but integral
or pointer types to variadic functions. See 5.2.2/7 for details.

Alex


Alf P. Steinbach

unread,
Jul 21, 2008, 3:09:10 PM7/21/08
to
* Ben Voigt [C++ MVP]:

:-)

I see Alex Blekhman already answered the technical: it's simply UB to pass
non-POD to "..." (except at compile time).

The reason for the UB is, presumably, exactly your reasoning, so it's good
reasoning.

But what I want to address is the "Alf's": it's simply not my idea. I now read
somewhere in MS documentation that said straight out that it's "impossible", so
evidently that's one of those memes floating around at Microsoft, a common
misconception that everyone agrees to because everyone agrees, and everybody's
conformist enough not to be able reason about such trivial thing!

As I recall it was done that way by SPL under MPE-IV on the HP3000, and a few
other examples, but since it's so long ago I'm not 100% sure -- take that
concret ref with a grain salt... :-)

Ben Voigt [C++ MVP]

unread,
Jul 21, 2008, 5:29:58 PM7/21/08
to
>> Since stdcall defines no such mechanism and reserves no location
>> (hidden param, designated register, stack slot) to accommodate that
>> additional information, any place you'd choose to pass the info would
>> have been up for grabs under the existing stdcall convention,
>
> No, not at all. There are only two cases to consider.
>
> Either (1) the language implementation already supports variadic
> stdcall routines, in which case it necessarily already does this. :-)

And possibly in a way incompatible with yours.

The only way you can guarantee that there is no pre-existing variadic
stdcall code you'd need to maintain compatibility with, is if in fact there
is no such thing as variadic stdcall code. Because if there were, it could
exist, and it could be trivially incompatible with your implementation (for
example, pass the number of bytes in the argument list in a 64-bit integer
vs size_t). Therefore, proof by contradiction, your assumption that there
is no existing variadic stdcall code requires that stdcall is in fact
incompatible with variable argument lists.

Alf P. Steinbach

unread,
Jul 21, 2008, 6:07:59 PM7/21/08
to
* Ben Voigt [C++ MVP]:
>>> Since stdcall defines no such mechanism and reserves no location
>>> (hidden param, designated register, stack slot) to accommodate that
>>> additional information, any place you'd choose to pass the info would
>>> have been up for grabs under the existing stdcall convention,
>> No, not at all. There are only two cases to consider.
>>
>> Either (1) the language implementation already supports variadic
>> stdcall routines, in which case it necessarily already does this. :-)
>
> And possibly in a way incompatible with yours.

First, I've posted in a follow-up to you elsewhere that I have no ownership.

Please don't use such intentionally misleading rhetorical techniques in future,
especially after having been notified not to do so. This paragraph is your
second notification about that particular issue. I must assume that you did
understand the first notification, and are therefore now doing it just as a
manipulative technique. TIA for stopping, if you can. I understand that this
manipulative tendency is perhaps ingrained in Microsoft corporate culture, at
least as displayed in this group, but it just takes some willpower to be honest.

Now, re your comment.

In the case of having support for stdcall variadic function in the language
implementation, one would not change the language implementation.

So while your earlier article in this thread displayed good reasoning but
reached the wrong conclusion due to erronous beliefs (you thought C++ supported
non-POD arguments to variadic functions), in this case both your reasoning and
conclusion is simply meaningless, and not due to mistaken beliefs about facts:
you have all the facts at hand, but still manage to produce idiocy.

Perhaps, considering that, I'd better repeat.

In the case of having support for stdcall variadic function in the language
implementation, one would not change the language implementation.


> The only way you can guarantee that there is no pre-existing variadic
> stdcall code you'd need to maintain compatibility with, is if in fact there
> is no such thing as variadic stdcall code.

This is a fallacious generalization, from no support in some hypothetical
language implementation -> no support anywhere.


> Because if there were, it could exist,

I'm impressed by this reasoning.


> and it could be trivially incompatible with your implementation (for
> example, pass the number of bytes in the argument list in a 64-bit integer
> vs size_t).

That's the third fallacy in a row. Of course, /you/ might go about changing a
language so it would become incompatible with existing software, as you're
arguing you would. But reasonable, competent folks would not.


> Therefore, proof by contradiction, your assumption that there
> is no existing variadic stdcall code requires that stdcall is in fact
> incompatible with variable argument lists.

That's fallacy number four, as I'm counting. Try to get some sleep, Ben. :-)


>> Or, (2), it does not, in which case with the language implemention
>> we're considering extending there are no existing calls to variadic
>> stdcall functions, no existing such functions, and therefore all at
>> the machine code level is free, within the constraints of argument
>> pushing order and stack adjustment.


Cheers,

- Alf


PS: In the follow-up to your eearlier article I wrote that it was "good
reasoning", which it was. I'm now speculating that perhaps, given the manifest
lack of reasoning powers you display above, you may have misunderstood my
comment to mean you had reached a correct conclusion then. You had not, the
conclusion there was just meaningless, due to you having your facts wrong.

Ben Voigt [C++ MVP]

unread,
Jul 21, 2008, 6:41:51 PM7/21/08
to
Alf P. Steinbach wrote:
> * Ben Voigt [C++ MVP]:
>>>> Since stdcall defines no such mechanism and reserves no location
>>>> (hidden param, designated register, stack slot) to accommodate that
>>>> additional information, any place you'd choose to pass the info
>>>> would have been up for grabs under the existing stdcall convention,
>>> No, not at all. There are only two cases to consider.
>>>
>>> Either (1) the language implementation already supports variadic
>>> stdcall routines, in which case it necessarily already does this.
>>> :-)
>>
>> And possibly in a way incompatible with yours.
>
> First, I've posted in a follow-up to you elsewhere that I have no
> ownership.

I see "a proposal for extending tool support for stdcall to permit variadic
functions". The calling convention stdcall is not yours, the proposal to
extend it is. Or no?

>
> Please don't use such intentionally misleading rhetorical techniques
> in future, especially after having been notified not to do so. This
> paragraph is your second notification about that particular issue. I
> must assume that you did understand the first notification, and are
> therefore now doing it just as a manipulative technique. TIA for
> stopping, if you can. I understand that this manipulative tendency is
> perhaps ingrained in Microsoft corporate culture, at least as
> displayed in this group, but it just takes some willpower to be
> honest.
> Now, re your comment.
>
> In the case of having support for stdcall variadic function in the
> language implementation, one would not change the language
> implementation.

A definite article has no place here (i.e. "the"). As Liviu has pointed out
repeatedly, there's not just one implementation. Many compilers, for
multiple languages, have support for stdcall.

Your entire rebuttal hinges on there being only one existing implementation
which either includes or excludes variable argument lists, and thus is
faulty.

I will present my argument in a simpler form to make it easier to respond
to.

Premise #1 -- An calling convention implements stdcall iff it in binary
compatible with every other conformant implementation.
Premise #2 -- If variadic functions are compatible with stdcall, then there
might exist a conformant implementation already (other than the released
versions of Microsoft Visual C++), which neither of us knows about.
Premise #3 -- You gave assembly code for an example of what you called a
variadic stdcall function.
Postulate #1 -- The argument for stdcall-conformance of the example of point
#3 is valid.
Consequence #1 -- If the example you gave were modified to pass the number
of succeeding arguments in an integer twice as wide, none of your arguments
for it being stdcall-conformant are affected, so your logic decrees that
this also is a variadic stdcall function.
Consequence #2 -- Since the example in point #4 is stdcall conformant, then
it might be implemented in some tools already.
Consequence #3 -- The stdcall-compliant example in point #3 and the related
stdcall-compliant example in point #4 are not binary-compatible.

QED, by contradiction of consequence #3 with point #1.

[snip]

> PS: In the follow-up to your eearlier article I wrote that it was
> "good reasoning", which it was. I'm now speculating that perhaps,
> given the manifest lack of reasoning powers you display above, you
> may have misunderstood my comment to mean you had reached a correct
> conclusion then. You had not, the conclusion there was just
> meaningless, due to you having your facts wrong.

I understand perfectly well that a valid argument + incorrect premises do
not a correct conclusion make.

The argument given here is also valid, as you will see. So which of the
premises do you dispute?


Alf P. Steinbach

unread,
Jul 21, 2008, 7:54:11 PM7/21/08
to
* Ben Voigt [C++ MVP]:
> Alf P. Steinbach wrote:
>> * Ben Voigt [C++ MVP]:
>>>>> Since stdcall defines no such mechanism and reserves no location
>>>>> (hidden param, designated register, stack slot) to accommodate that
>>>>> additional information, any place you'd choose to pass the info
>>>>> would have been up for grabs under the existing stdcall convention,
>>>> No, not at all. There are only two cases to consider.
>>>>
>>>> Either (1) the language implementation already supports variadic
>>>> stdcall routines, in which case it necessarily already does this.
>>>> :-)
>>> And possibly in a way incompatible with yours.
>> First, I've posted in a follow-up to you elsewhere that I have no
>> ownership.
>
> I see "a proposal for extending tool support for stdcall to permit variadic
> functions". The calling convention stdcall is not yours, the proposal to
> extend it is. Or no?

No to both and more.

The calling convention is not mine,

the way to implement it that I've demonstrated is not mine,

I have not made any proposal (on the contrary, I've cautioned against that, but
seemingly for very deaf ears),

and your quote, if it is meant as a quote, is certainly not quoting me -- I
think it's something you made up.


>> Please don't use such intentionally misleading rhetorical techniques
>> in future, especially after having been notified not to do so. This
>> paragraph is your second notification about that particular issue. I
>> must assume that you did understand the first notification, and are
>> therefore now doing it just as a manipulative technique. TIA for
>> stopping, if you can. I understand that this manipulative tendency is
>> perhaps ingrained in Microsoft corporate culture, at least as
>> displayed in this group, but it just takes some willpower to be
>> honest.
>> Now, re your comment.
>>
>> In the case of having support for stdcall variadic function in the
>> language implementation, one would not change the language
>> implementation.
>
> A definite article has no place here (i.e. "the").

The definite article refers to the hypothetical language implementation under
discussion.

English is not my native language, but you're really grasping at straws here.


> As Liviu has pointed out
> repeatedly, there's not just one implementation. Many compilers, for
> multiple languages, have support for stdcall.

I fail to see the relevance to any aspect of the /technical/ matters discussed.
The Earth is round, the moon is there, one must assume the galaxy is still
hanging on. Those are all trivially true facts, but by mentioning particular
trivially true facts, in a context where it seems you're arguing against me, I'm
pretty sure you're trying to be manipulative again.

I.e., talking to an audience, trying to make readers believe I must, somewhere
earlier, have maintained the opposite of your mentioned trivially true facts.

Why not try to be honest for a change?


> Your entire rebuttal hinges on there being only one existing implementation
> which either includes or excludes variable argument lists, and thus is
> faulty.

No, that's at best a misunderstanding on your part.

But since this doesn't even make sense as manipulative
make-readers-believe-some-silly-thing-about-Alf's-arguments, I think you're
perhaps sleep-deprived or something.

I mean, you have written meaningful things before, even insightful things, as I
recall.

So I'll go easy, I'm assuming sleep deprivation or tiredness or something on
your part.

Anyway, first, even if had made such an idiotic argument, which of course I
haven't (this is Usenet, with complete history available for those who want to
check), even if I had made such an idiotic argument it wouldn't affect reality.

And second, your article contained four individual fallacies which I pointed out
individually, no one individual pointing-out-of-fallacy relying on others.


> I will present my argument in a simpler form to make it easier to respond
> to.

Good, thank you.


> Premise #1 -- An calling convention implements stdcall iff it in binary
> compatible with every other conformant implementation.

This either places very strong constraints on "binary compatible", or is simply
wrong.

In general it's simply wrong -- but see comment at end of this section below.

Although I don't consider mangled names to be part of stdcall convention, first
example that binary compatibility doesn't hold for that. g++ produces mangled
name "__Z3food@8" for "void __stdcall foo( double ) {}", whereas MSVC produces
mangled name "?foo@@YGXN@Z". I'm sorry for addressing since you don't bring it
explicitly up, but the vagueness of "binary compatible" means it could mean just
about anything, so, if you need even more forceful arg about that, consider e.g.
not-mangled names of MessageBox routines in user32.dll.

Arguing against myself, it's not unreasonable to consider /C/ mangled names as
part of stdcall convention.

Arguing back against myself, hey, that's a neat notion, but only for a subset of
cases; e.g. it falls flat on its face when compared to the Windows API reality,
where names are not mangled that way, but are certainly stdcall.

Then, if hopefully that's a complete enough exposition of the mangling as part
of convention or not (yes in some cases, no in general, e.g. for Windows API),
example of different machine code for same __stdcall routine.

#include <iostream>

struct Blah { int x; Blah(): x(666) {} };

Blah __stdcall foo() { Blah x; return x; } // This routine.

int main()
{
Blah const b = foo();
std::cout << b.x << std::endl;
}

Here g++ (default options) returns the result in register EAX,

.def __Z3foov@0; .scl 2; .type 32; .endef
__Z3foov@0:


push ebp
mov ebp, esp

mov eax, 666
pop ebp
ret

while MSVC (default options) employs RVO where the caller must supply the
address where the result should be placed,

_x$ = -4 ; size = 4
___$ReturnUdt$ = 8 ; size = 4
?foo@@YG?AUBlah@@XZ PROC NEAR ; foo


push ebp
mov ebp, esp

push ecx
lea ecx, DWORD PTR _x$[ebp]
call ??0Blah@@QAE@XZ ; Blah::Blah
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
mov ecx, DWORD PTR _x$[ebp]
mov DWORD PTR [eax], ecx
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
mov esp, ebp
pop ebp
ret 4

I hope you understand this, that in the g++ case above the caller pushes
nothing, calls foo() and gets a result back in register eax, while in the MSVC
case the caller must push the caller's result storage address before calling
foo, i.e., that the generated machine code for the two tools *is not binary
compatible* in spite of both tools generating perfectly acceptable stdcall.

I can elaborate if you want, but the point is, evidenced by facts above, your
premise #1 is simply wrong in general -- although, I hasten the emphasise, it
*is* an important consideration for a large subset of cases.

It's quite natural to make such a mistaken generalization.

But now, perhaps you know better, yes? ;-)


> Premise #2 -- If variadic functions are compatible with stdcall, then there
> might exist a conformant implementation already (other than the released
> versions of Microsoft Visual C++), which neither of us knows about.

Right.


> Premise #3 -- You gave assembly code for an example of what you called a
> variadic stdcall function.

Right, and not just "called" a variadic stdcall function: it was, conforming to
every stdcall requirement.


> Postulate #1 -- The argument for stdcall-conformance of the example of point
> #3 is valid.

Right.


> Consequence #1 -- If the example you gave were modified to pass the number
> of succeeding arguments in an integer twice as wide, none of your arguments
> for it being stdcall-conformant are affected, so your logic decrees that
> this also is a variadic stdcall function.

Right.


> Consequence #2 -- Since the example in point #4 is stdcall conformant, then
> it might be implemented in some tools already.

Right.


> Consequence #3 -- The stdcall-compliant example in point #3 and the related
> stdcall-compliant example in point #4 are not binary-compatible.

Right.


> QED, by contradiction of consequence #3 with point #1.

Point #1 does not hold. :-)

> [snip]
>
>> PS: In the follow-up to your eearlier article I wrote that it was
>> "good reasoning", which it was. I'm now speculating that perhaps,
>> given the manifest lack of reasoning powers you display above, you
>> may have misunderstood my comment to mean you had reached a correct
>> conclusion then. You had not, the conclusion there was just
>> meaningless, due to you having your facts wrong.
>
> I understand perfectly well that a valid argument + incorrect premises do
> not a correct conclusion make.
>
> The argument given here is also valid, as you will see. So which of the
> premises do you dispute?

See above.

You have a faulty premise #1.

Plus (although that's not necessary to discuss to invalidate the conclusion)

you have too abstract terminology, a term that can mean different things in
different contexts, "binary compatibility". It's like the term "something", as
used in "We must do something, this is something, ergo, we must do this".

So, there's an important addendum: even if point #1 held (which as demonstrated
by actual examples above it does not), then /breaking/ binary compatibility is
something very different from having two routines that are not binary
compatible. In order to break compatibility the compatibility must be used, and
something must be changed so that the existing usage doesn't work anymore. That
would not be the case anyway, unless done by design in order to achieve that.


Cheers, & rhth.,

- Alf

PS: Thanks very much for trying to make a clear case. Hopefully that's also made
my response, & reality!, more grokkable. If not, then I'll elaborate on request.

Liviu

unread,
Jul 21, 2008, 8:59:18 PM7/21/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>>>
>>> We're discussing what stdcall can (or "could") support, not,
>>> hopefully, what Visual C++ supports today.
>>
>> We're discussing what stdcall can and does support. Not what it
>> "could" support if one oddly decided to label a new convention as
>> "stdcall".
>
> Let's be clear.
>
> What stdcall does support is (1) what it's documented as supporting, +
> (2) what Visual C++ implicitly lets it support (that is mostly stdcall
> for member functions, used in COM, but perhaps it's documented
> separately).
>
> What stdcall can support is something else. Then we are into the
> "could". stdcall as a calling convention can support variadic
> functions, and Visual C++ could therefore support stdcall variadic
> functions (but doesn't).

Stdcall does not, can not and could not support variadic functions.
An extension to stdcall could, but at the price of breaking binary
compatiblity with stdcall.

>>> And as demonstrated all that's necessary is for the compiler to pass
>>> the number of argument bytes, in some way, when it has a variadic
>>> function at hand.
>>
>> Please elaborate that "in some way" part. My point was that it's not
>> possible without breaking binary compatibility with existing stdcall.
>> Be welcome to "demonstrate" otherwise
>

> For example in a register.

Any change to stdcall which requires reserving a register previously
available for general purpose use is a break of the existing convention.

> Please also note, if you're going to raise this question again, then
> please include a concrete example that illustrates how you think
> something can be broken, because arguing against or trying to explain
> some unknown hypotetical is just not constructive.

Say the register you propose that stdcall appropriates is EXX.
Then assume an instrumentation function

; EAX = address of target function, either stdcall or cdecl
instrument_target:
; ... local processing here which uses/changes EXX
; ... and does not modify the stack
jmp EAX ; jump to target, which then returns to caller

which would be called from assembly code as a "trampoline" into the real
target (for whatever reason - custom delayed loader, interception a la
detours etc). Note that instrument_target makes no assumptions about the
stack layout, and rightfully claims to support any stdcall or cdecl
target function - which it does.

But, once you add a requirement that EXX must be preserved/passed
through for some "stdcall" calls, and recompile client code using
instrument_target to use the new convention, it will badly fail in those
cases. So perfectly legit code according to the existing specs would
break because you redefined "stdcall". Thus (stdcall + EXX) != stdcall.

Cheers,
Liviu

Alf P. Steinbach

unread,
Jul 21, 2008, 10:21:48 PM7/21/08
to
* Liviu:

> "Alf P. Steinbach" <al...@start.no> wrote
>> * Liviu:
>>>> We're discussing what stdcall can (or "could") support, not,
>>>> hopefully, what Visual C++ supports today.
>>> We're discussing what stdcall can and does support. Not what it
>>> "could" support if one oddly decided to label a new convention as
>>> "stdcall".
>> Let's be clear.
>>
>> What stdcall does support is (1) what it's documented as supporting, +
>> (2) what Visual C++ implicitly lets it support (that is mostly stdcall
>> for member functions, used in COM, but perhaps it's documented
>> separately).
>>
>> What stdcall can support is something else. Then we are into the
>> "could". stdcall as a calling convention can support variadic
>> functions, and Visual C++ could therefore support stdcall variadic
>> functions (but doesn't).
>
> Stdcall does not, can not and could not support variadic functions.
> An extension to stdcall could, but at the price of breaking binary
> compatiblity with stdcall.

Sorry, but those assertions are rubbish. ;-)


>>>> And as demonstrated all that's necessary is for the compiler to pass
>>>> the number of argument bytes, in some way, when it has a variadic
>>>> function at hand.
>>> Please elaborate that "in some way" part. My point was that it's not
>>> possible without breaking binary compatibility with existing stdcall.
>>> Be welcome to "demonstrate" otherwise
>> For example in a register.
>
> Any change to stdcall which requires reserving a register previously
> available for general purpose use is a break of the existing convention.

Sorry again, but that's rubbish -- well, unless you're talking of actually
changing stdcall, as your formulation implies, which would just be stupid.


>> Please also note, if you're going to raise this question again, then
>> please include a concrete example that illustrates how you think
>> something can be broken, because arguing against or trying to explain
>> some unknown hypotetical is just not constructive.
>
> Say the register you propose that stdcall appropriates is EXX.
> Then assume an instrumentation function
>
> ; EAX = address of target function, either stdcall or cdecl
> instrument_target:
> ; ... local processing here which uses/changes EXX
> ; ... and does not modify the stack
> jmp EAX ; jump to target, which then returns to caller
>
> which would be called from assembly code as a "trampoline" into the real
> target (for whatever reason - custom delayed loader, interception a la
> detours etc). Note that instrument_target makes no assumptions about the
> stack layout, and rightfully claims to support any stdcall or cdecl
> target function - which it does.

Maybe, let's assume that for the sake of discussion, its claim is currently,
with all tested code so far, good.


> But, once you add a requirement that EXX must be preserved/passed

> through for some [existing] "stdcall" calls, and recompile client code using


> instrument_target to use the new convention, it will badly fail in those
> cases.

Yes, it would be stupid, idiotic, braindead, etc. ad nauseam, to introduce a
solution that Does Not Work, imposing new requirements on existing code.

I think we've discussed this before.

Your demonstrations that you're able to Make Things Not Work are impressive as
demonstrations of that -- I couldn't have come up with them! <g> -- but
really, who would be stupid enough to do change the instrumented client code so
that it would stop working?

Note that e.g. a

void __really __stdcall foo( ... );

would not be affected in this scheme, by the simple fact that it can't already
exist when support for stdcall variadic functions is introduced.

Hence, by not existing, it's not instrumented, not affected: no binary
compatibility is broken by introducing support for them in a reasonable manner,
as opposed to your general changing of stdcall convention for existing routines.

What's affected by supporting foo() in a particular way incompatible with the
bad trampoline code, is just that the bad instrumentation code now only can
claim to support non-variadic stdcall functions. That's not less than it (by
assumption) supported before. But if its documentation is of the same low
quality as its implementation, the documentation may have to be upgraded.

However, on a more constructive note, if we make this more real and talk about
practical solutions for Visual C++, which in a braindead manner changes the
meaning of __stdcall to mean __cdecl in the context of a variadic function, then
in a short-sighted perspective a practical solution is to call stdcall something
else in this contex, e.g. __stdcallv. The name of the animal does not matter
much as long as it answers to its name: it would be stdcall anyway, just under a
name that preserved the meaning of documentation for badly written trampoline
code. Many such compromises have been made by Microsoft. I think they're ungood
because they accumulate and then in Microsoft world you have to relate to
arbitrary terminology, with non-systematic special-cased meanings of terms[1].

Some of the same terminology sickness, catering to bad earlier decisions, has
unfortunately also entered e.g. the C++ standard, and e.g. the C++ FAQ, so it's
not limited to te Microsoft world, although most prevalent there.


> So perfectly legit code according to the existing specs

I wonder where the specs are that says which registers a forwarder can and
cannot freely change for stdcall, do you have a reference?

Anyways, even though you do seem to like code that doesn't work, if you want the
trampoline to also work with new stdcall variadic functions (not sure why they
would be so important to support compared to the host of other routines not
supported?), the best solution would be to fix the bad trampoline code.

Next best, use some slightly less efficient way to pass arguments size, instead
of register.


> would
> break because you redefined "stdcall". Thus (stdcall + EXX) != stdcall.

Yes, you have managed to break existing code by changing stdcall convention for
existing routines, kudos for yet another example of code that Does Not Work, and

no, no existing code would be broken by just introducing support for stdcall
variadic functions.

However, regarding the latter, possibly some documentation of a very nasty bad
piece of trampoline code would be be exposed as claiming a little too much.

That's not a big deal, and probably for the best. :-)


Cheers, & hth.,

- Alf

Notes:
[1] See <url: http://en.wikipedia.org/wiki/Commons_dilemma> for relevant
discussion. I think this is the single most important factor contributing to the
Microsoft empire's impending downfall. Oh, forget to mention, my name's Seldon,
Hari Seldon. ;-)

Alexander Grigoriev

unread,
Jul 21, 2008, 10:58:37 PM7/21/08
to

"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:eN7o$7z6IH...@TK2MSFTNGP06.phx.gbl...

> Liviu wrote:
>>>
>>> Yes, it does make sense to destroy those inside the function. That
>>> saves code size.
>>
>> Also makes sense in terms of exception handling, so that he who
>> compiles f() doesn't have to rely on caller's mercy (and /E options)
>> to dispose of temporaries should an exception actually be thrown.
>
> But this ought to be very interesting in conjunction with variadic
> functions... because the arguments must be destructed by the caller.
>
> I believe this requirement in the standard will be the death-blow to Alf's
> so-called variadic __stdargs.


Variadic arguments are POD-only. That's by the standard.


Liviu

unread,
Jul 21, 2008, 11:06:29 PM7/21/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Liviu:
>>
>> Stdcall does not, can not and could not support variadic functions.
>> An extension to stdcall could, but at the price of breaking binary
>> compatiblity with stdcall.
>
> Sorry, but those assertions are rubbish. ;-)

Sorry, but those words are hollow ;-)

>> But, once you add a requirement that EXX must be preserved/passed
>> through for some [existing] "stdcall" calls, and recompile client
>> code using instrument_target to use the new convention, it will badly
>> fail in those cases.
>
> Yes, it would be stupid, idiotic, braindead, etc. ad nauseam, to
> introduce a solution that Does Not Work, imposing new requirements on
> existing code.

Right. The instrument_target code, which is language and compiler
neutral, would break because of a change in the semantics of stdcall.
Note that said change (reserving the EXX register) would not affect in
any way the binary code of instrument_target itself (as could be present
in a .lib or .dll), yet it would break unsuspecting client code trying
to use instrument_target like it always did before such a "stdcall"
redefinition. Which is exactly why no one would ever imagine "extending"
stdcall in a binary incompatible way. Oh well, _almost_ no one ;-)

> Note that e.g. a
>
> void __really __stdcall foo( ... );
>
> would not be affected in this scheme

At the risk of restating the obvious, but "void __knurre foo(...);"
would not break anything at all, in C/C++ or elsewhere.

> Hence, by not existing, it's not instrumented, not affected: no binary
> compatibility is broken by introducing support for them in a
> reasonable manner, as opposed to your general changing of stdcall
> convention for existing routines.

I was not (and the given example was not) referring to any particular
routine. It was about an arbitrary stdcall, which covers any function
present or future which conforms to the stdcall convention.

> What's affected by supporting foo() in a particular way incompatible
> with the bad trampoline code, is just that the bad instrumentation
> code now only can claim to support non-variadic stdcall functions.

Redundant.

> However, on a more constructive note, if we make this more real and
> talk about practical solutions for Visual C++, which in a braindead
> manner changes the meaning of __stdcall to mean __cdecl in the context
> of a variadic function, then in a short-sighted perspective a
> practical solution is to call stdcall something else in this contex,
> e.g. __stdcallv.

OK, then I'd call this case closed.

>> So perfectly legit code according to the existing specs
>
> I wonder where the specs are that says which registers a forwarder can
> and cannot freely change for stdcall, do you have a reference?

You'll find some at
http://msdn.microsoft.com/en-us/library/aa295770.aspx. For subsets of
stdcall families (such as the Win32 API) there are more rules, of
course. Point is however that anything which is not declared as "used"
or "reserved" is considered to be available for use. Since there is no
register marked as "reserved for future support of variadic functions",
adding one now is not possible - short of changing the spec.

> Anyways, even though you do seem to like code that doesn't work, if
> you want the trampoline to also work with new stdcall variadic
> functions (not sure why they would be so important to support compared
> to the host of other routines not supported?), the best solution would
> be to fix the bad trampoline code.

What's bad about it? It is, again, perfectly correct code under the
stdcall specs.

> no, no existing code would be broken by just introducing support for
> stdcall variadic functions.
>
> However, regarding the latter, possibly some documentation of a very
> nasty bad piece of trampoline code would be be exposed as claiming a
> little too much.
>
> That's not a big deal, and probably for the best. :-)

So it's no longer "no existing code would be broken", now it's "no
'worthy' existing code would be broken". I rest my case.

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 22, 2008, 12:05:29 AM7/22/08
to
* Liviu:
> "Alf P. Steinbach" <al...@start.no> wrote
>> * Liviu:
>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>> * Liviu:
>>> Stdcall does not, can not and could not support variadic functions.
>>> An extension to stdcall could, but at the price of breaking binary
>>> compatiblity with stdcall.
>> Sorry, but those assertions are rubbish. ;-)
>
> Sorry, but those words are hollow ;-)

Please stop posting rubbish.


>>> But, once you add a requirement that EXX must be preserved/passed
>>> through for some [existing] "stdcall" calls, and recompile client
>>> code using instrument_target to use the new convention, it will badly
>>> fail in those cases.
>> Yes, it would be stupid, idiotic, braindead, etc. ad nauseam, to
>> introduce a solution that Does Not Work, imposing new requirements on
>> existing code.
>
> Right. The instrument_target code, which is language and compiler
> neutral, would break because of a change in the semantics of stdcall.

Are you saying the instrument_target code uses stdcall variadic routines,
/before/ support for that exists in the language implementation?

Or are you saying the instrumented code uses such routines, /before/ support for
that exists in the language implementation?

It's infuriating that you're pretending to not grasp the simplest points, and
just go on re-asserting the same old rubbish.


> Note that said change (reserving the EXX register) would not affect in
> any way the binary code of instrument_target itself (as could be present
> in a .lib or .dll), yet it would break unsuspecting client code trying
> to use instrument_target like it always did before such a "stdcall"
> redefinition.

Why the heck would you reserve the EXX (any E...) register in general?


> Which is exactly why no one would ever imagine "extending"
> stdcall in a binary incompatible way. Oh well, _almost_ no one ;-)

Just you.

I don't think anyone else is that much into creating code and imagining
solutions that do not work.

>> Note that e.g. a
>>
>> void __really __stdcall foo( ... );
>>
>> would not be affected in this scheme
>
> At the risk of restating the obvious, but "void __knurre foo(...);"
> would not break anything at all, in C/C++ or elsewhere.

I presume that's meant as a counter-example to something else breaking.

But in spite of being a master at creating code that does not work, you've still
not reached your goal of breaking any code.

That fact that solutions that do not work are the only ones you admit to
understand does not mean that things cannot work, it only says something about
/you/.

>> Hence, by not existing, it's not instrumented, not affected: no binary
>> compatibility is broken by introducing support for them in a
>> reasonable manner, as opposed to your general changing of stdcall
>> convention for existing routines.
>
> I was not (and the given example was not) referring to any particular
> routine.

And nobody's said you were referring to just a particular routine, and nobody's
assumed you were. Feel happy. You have not been misunderstood in that respect,
at least not here. :-)


> It was about an arbitrary stdcall, which covers any function
> present or future which conforms to the stdcall convention.

That's not a meaningful statement. What is "arbitrary stdcall" anyway?


>> What's affected by supporting foo() in a particular way incompatible
>> with the bad trampoline code, is just that the bad instrumentation
>> code now only can claim to support non-variadic stdcall functions.
>
> Redundant.

That's not a meaningful statement, in fact, it's malformed as a sentence.


>> However, on a more constructive note, if we make this more real and
>> talk about practical solutions for Visual C++, which in a braindead
>> manner changes the meaning of __stdcall to mean __cdecl in the context
>> of a variadic function, then in a short-sighted perspective a
>> practical solution is to call stdcall something else in this contex,
>> e.g. __stdcallv.
>
> OK, then I'd call this case closed.

You're confused again. But if you're happy, so be it.


>>> So perfectly legit code according to the existing specs
>> I wonder where the specs are that says which registers a forwarder can
>> and cannot freely change for stdcall, do you have a reference?
>
> You'll find some at
> http://msdn.microsoft.com/en-us/library/aa295770.aspx.

Perhaps Internet Explorer shows more there than my Firefox, but at least in
Firefox there's nothing there about general register usage.


> For subsets of
> stdcall families (such as the Win32 API) there are more rules, of
> course.

Just out of curiousity, do you have any reference of the rules that you say are
in addition to the empty rule set you already provided a link to?


> Point is however that anything which is not declared as "used"
> or "reserved" is considered to be available for use.

Do you have a reference for that?


> Since there is no
> register marked as "reserved for future support of variadic functions",
> adding one now is not possible - short of changing the spec.

If what you've stated earlier here should turn out to be correct, this
conclusion would still be utter rubbish and idiocy.


>> Anyways, even though you do seem to like code that doesn't work, if
>> you want the trampoline to also work with new stdcall variadic
>> functions (not sure why they would be so important to support compared
>> to the host of other routines not supported?), the best solution would
>> be to fix the bad trampoline code.
>
> What's bad about it? It is, again, perfectly correct code under the
> stdcall specs.

You've yet to show that your code is correct. Anyways, good code in general has
a property called *robustness*. It doesn't avail itself of pecularities of a
given small context of usage, but is designed to work in general, assuming the
least instead of the most about its environment. Bad code is unable to handle
the slightest provocation or change in usage. Your code was bad: it handled
particular cases, when it could easily have handled a much broader set of cases.

But still your code did not break.

It would just be unable to handle more cases than it already was used for, as is
usual for bad code.


>> no, no existing code would be broken by just introducing support for
>> stdcall variadic functions.
>>
>> However, regarding the latter, possibly some documentation of a very
>> nasty bad piece of trampoline code would be be exposed as claiming a
>> little too much.
>>
>> That's not a big deal, and probably for the best. :-)
>
> So it's no longer "no existing code would be broken", now it's "no
> 'worthy' existing code would be broken".

The latter quote is not quoting me.

Nor is it representing anything I've written or indicated, nor is it in tune
with reality.

It is a lie.


> I rest my case.

Well, that may be a good idea, when you have to resort to umpteen reassertions
of old meaningless rubbish, plus lying (right above).

I don't understand how you can do this.


Cheers,

- Alf

Liviu

unread,
Jul 22, 2008, 2:07:47 AM7/22/08
to

"Alf P. Steinbach" <al...@start.no> wrote in message
news:SOOdnZ2_8uUQwRjV...@posted.comnet...

>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Liviu:
>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>> * Liviu:
>
>>>> But, once you add a requirement that EXX must be preserved/passed
>>>> through for some [existing] "stdcall" calls, and recompile client
>>>> code using instrument_target to use the new convention, it will
>>>> badly fail in those cases.
>>>
>>> Yes, it would be stupid, idiotic, braindead, etc. ad nauseam, to
>>> introduce a solution that Does Not Work, imposing new requirements
>>> on existing code.
>>
>> Right. The instrument_target code, which is language and compiler
>> neutral, would break because of a change in the semantics of stdcall.
>
> Are you saying the instrument_target code uses stdcall variadic
> routines, /before/ support for that exists in the language
> implementation?

I am saying that the given instrument_target code supports stdcall
routines, period. Your need to introduce additional qualifiers such as
"/before/ support for [variadic]" makes it only more obvious that what
you are talking about is _not_ stdcall.

> Or are you saying the instrumented code uses such routines, /before/
> support for that exists in the language implementation?

"/before/" or not is meaningless here. The given code is generic, and
works for any stdcall or cdecl functions, present or future.

>> Note that said change (reserving the EXX register) would not affect
>> in any way the binary code of instrument_target itself (as could be
>> present in a .lib or .dll), yet it would break unsuspecting client
>> code trying to use instrument_target like it always did before such a
>> "stdcall" redefinition.
>
> Why the heck would you reserve the EXX (any E...) register in general?

Umm... That's straight from your "for example in a register" answer to
the question of how variadic functions would receive the number of
actual arguments. Seems quite obvious that you cannot use a register
unless you somehow reserve it, so that related code knows that it's off
limits to them.

And if by "in general" you mean that the "new stdcall" would have
different register usage specs vs. "stdcall" then, duh. That's exactly
what would make the "new stdcall" not binary compatible with stdcall.

>> I was not (and the given example was not) referring to any particular

>> routine. It was about an arbitrary stdcall, which covers any function


>> present or future which conforms to the stdcall convention.
>
> That's not a meaningful statement. What is "arbitrary stdcall" anyway?

A call to a stdcall function not known at compile time, and about which
no additional assumptions are made except that it's stdcall.

For all I care, the client code could ask the user to type in the name
and signature of an arbitrary stdcall function exported from msvcrt.dll,
push arguments consistent with the signature onto the stack, fetch the
function address at runtime from msvcrt.dll and copy it into EAX, then
call instrument_target - and expect the call to eventually land into the
given function and work correctly. That would work regardless of whether
the function in question existed or not when instrument_target was
written. All that matters is that the function is stdcall.

>>> a practical solution is to call stdcall something else in this
>>> contex, e.g. __stdcallv.
>>
>> OK, then I'd call this case closed.
>
> You're confused again. But if you're happy, so be it.

Not at all. Call it __stdcallv, and any objections about compatibility
would become moot.

>>>> So perfectly legit code according to the existing specs
>>>
>>> I wonder where the specs are that says which registers a forwarder
>>> can and cannot freely change for stdcall, do you have a reference?
>>
>> You'll find some at
>> http://msdn.microsoft.com/en-us/library/aa295770.aspx.
>
> Perhaps Internet Explorer shows more there than my Firefox, but at
> least in Firefox there's nothing there about general register usage.

|| Registers:
|| this (thiscall only) ECX
|| not used EDX

>> For subsets of stdcall families (such as the Win32 API) there are
>> more rules, of course.
>
> Just out of curiousity, do you have any reference

http://support.microsoft.com/kb/232587/en-us

>> Point is however that anything which is not declared as "used"
>> or "reserved" is considered to be available for use.
>
> Do you have a reference for that?

Google for "what's not occupied is available" ;-)

>>> if you want the trampoline to also work with new stdcall variadic

I just want the code to keep working with stdcall functions. Yours "new
stdcall variadic" are none such.

>>> the best solution would be to fix the bad trampoline code.
>>
>> What's bad about it?
>

> You've yet to show that your code is correct.

Do you really think it's not, or are you just trol^H^H^H^Hkidding.

> Anyways, good code in general has a property called *robustness*.
> It doesn't avail itself of pecularities of a given small context of
> usage, but is designed to work in general, assuming the least instead
> of the most about its environment.

Oh, the irony. It is _you_ who in effect is saying "gee, here is a
register I am going to steal for calling variadic functions with my new
convention, I bet no one knew that register existed, so I'll just assume
no existing code would break if I misrepresent my new convention as
stdcall".

Cheers,
Liviu


Alf P. Steinbach

unread,
Jul 22, 2008, 4:00:05 AM7/22/08
to

You've demonstrated that your code does not work generically for all stdcall
functions, as I understand the term -- because it mangles register values
willy-nilly. ;-)

Anyway, I listed a few ways you could have implemented stdcall variadic routines
so they would work also with your willy-nilly register-modifying trampoline.

As mentioned a great many times already, your ability to invent ways of not
solving a trivial problem doesn't mean the trivial problem can't be solved. In
fact trivial problems are trivial to solve. For the rest of us, I mean.


>>> Note that said change (reserving the EXX register) would not affect
>>> in any way the binary code of instrument_target itself (as could be
>>> present in a .lib or .dll), yet it would break unsuspecting client
>>> code trying to use instrument_target like it always did before such a
>>> "stdcall" redefinition.
>> Why the heck would you reserve the EXX (any E...) register in general?
>
> Umm... That's straight from your "for example in a register" answer to
> the question of how variadic functions would receive the number of
> actual arguments. Seems quite obvious that you cannot use a register
> unless you somehow reserve it, so that related code knows that it's off
> limits to them.

If you choose to use a register for passing the size info to variadic routines,
all that's needed is to document that for this language implementation, a
stdcall variadic routine uses this register, say, ESI; you don't need to reserve
the register in general.

Then, for your trampoline code, it's up to users whether they then decide to
pass it a variadic stdcall routine to call. Given the fact that you're actively
designing it not to be able to handle that, I as a user would simply not use it
for such new stdcall variadic routines. If I were already using it for something
else, it would still work for that, assuming it did work.

But if that's too fine or practical a point for you, by all means, as mentioned,
assume that the size info is passed by pushing it on stack, as in my working
example code at the start of this subthread.


> And if by "in general" you mean that the "new stdcall" would have
> different register usage specs vs. "stdcall" then, duh.

The stdcall convention has a small and simple set of requirements: for caller,
push right to left, for callee, adjust stack pointer back, and, the only
documentation I've found about register usage[1], preserve all other registers
except eax, ecx and edx. I see below that you provide another MS reference that
says preserve only specific four registers. Well, be conservative.

If a routine complies with that, then as far as the documention I've found says,
it's stdcall.


> That's exactly
> what would make the "new stdcall" not binary compatible with stdcall.

It is possible but only barely possible that you think that two source code
identical routines compiled as __stdcall with different compilers or compiler
option sets must be binary compatible.

That's not the case with g++ and msvc, which are the main ones I use, and you're
into fantasy-land if you maintain that what they're producing isn't stdcall.

Please repeat some 580 times or so: it's impossible to break binary
compatibility with code that does not yet exist.

You either have a language implementation that supports stdcall variadic
routines, in which case you're not changing anything and not breaking binary
compatibility.

Or you have a language implementation that does not yet support stdcall variadic
routines, and hence you have no such routines and no calls to such routines, and
so can't break binary compatibility with the non-existing calls to the
non-existing routines.


>>> I was not (and the given example was not) referring to any particular
>>> routine. It was about an arbitrary stdcall, which covers any function
>>> present or future which conforms to the stdcall convention.
>> That's not a meaningful statement. What is "arbitrary stdcall" anyway?
>
> A call to a stdcall function not known at compile time, and about which
> no additional assumptions are made except that it's stdcall.

You know, that problem pops up all the time in practical programming, every day.
Yep, don't know the function at compile time, check; and yep, don't know
anything more than that it's stcall, check; and yep, must call it, check.

Anyway, the proper way to deal with "no assumptions" is to make no assumptions.

Your code on the contrary assumed that anything that wasn't documented as being
of importance to a stdcall function would be up for grabs to destroy. The state
of the documentation being what it is, with errors and omissions and
inconsistencies galore, well that's a pretty bad approach. You're not talking
about the old MS COM-interceptor, are you?


> For all I care, the client code could ask the user to type in the name
> and signature of an arbitrary stdcall function exported from msvcrt.dll,
> push arguments consistent with the signature onto the stack, fetch the
> function address at runtime from msvcrt.dll and copy it into EAX, then
> call instrument_target - and expect the call to eventually land into the
> given function and work correctly. That would work regardless of whether
> the function in question existed or not when instrument_target was
> written. All that matters is that the function is stdcall.
>
>>>> a practical solution is to call stdcall something else in this
>>>> contex, e.g. __stdcallv.
>>> OK, then I'd call this case closed.
>> You're confused again. But if you're happy, so be it.
>
> Not at all. Call it __stdcallv, and any objections about compatibility
> would become moot.

Ouch. As mentioned quite a few times, I don't think it's a good idea to
implement this. But I think that for what I believe to be valid reasons, given
earlier, not invalid techno-babble-philosophical objections.

So perhaps I should not have mentioned this about keywords.

But note that keywords, in e.g. Visual C++ or some Pascal version, are not the
same as calling conventions. They /denote/ calling conventions.


>>>>> So perfectly legit code according to the existing specs
>>>> I wonder where the specs are that says which registers a forwarder
>>>> can and cannot freely change for stdcall, do you have a reference?
>>> You'll find some at
>>> http://msdn.microsoft.com/en-us/library/aa295770.aspx.
>> Perhaps Internet Explorer shows more there than my Firefox, but at
>> least in Firefox there's nothing there about general register usage.
>
> || Registers:
> || this (thiscall only) ECX
> || not used EDX
>
>>> For subsets of stdcall families (such as the Win32 API) there are
>>> more rules, of course.
>> Just out of curiousity, do you have any reference
>
> http://support.microsoft.com/kb/232587/en-us

Hm, thanks, says nearly but not exactly the same as the one I found[1]. Neither
article says anything about which registers a trampoline can trample on on the
way in. They only say what registers must be preserved on the way out, and
contradict each other on that; however, preserving all must be safe.

The prudent thing to do is then to not trample on any registers on the way in,
nor on the way out.

That's the same as the prudent thing to do in the absence of that documentation.


>>> Point is however that anything which is not declared as "used"
>>> or "reserved" is considered to be available for use.
>> Do you have a reference for that?
>
> Google for "what's not occupied is available" ;-)

I think you have that backwards for a trampoline function.

It needs to do the opposite, to consider anything it isn't expressly allowed to
trample on, off-limits.


>>>> if you want the trampoline to also work with new stdcall variadic
>
> I just want the code to keep working with stdcall functions. Yours "new
> stdcall variadic" are none such.

That quote is not a quote of me.

The variadic functions are not new, it's very very old stuff (not from the
Egyptian faraohs, but nearly).

A stdcall variadic function is just that, a varidia function fulfilling all
requirements of stdcall convention. If in your view that means not being passed
information via a register, then why not pass it on the stack. My working
example code at the start of this subthread did exactly that -- although I
disagree that it's necessary to go that length to support dirty bad trampoline
code, that disagreement is a very very minor practical point of implementation,
trivial (as this all is, by the way).


>>>> the best solution would be to fix the bad trampoline code.
>>> What's bad about it?
>> You've yet to show that your code is correct.
>
> Do you really think it's not, or are you just trol^H^H^H^Hkidding.

I think that trampoline code is not good, because you've stated that it can
trample on any registers (except critical ones like esp) on the way in.

That's not a good thing to do.

It wouldn't be a good thing to do even if some stdcall documentation expressly
allowed it to, because it can cause it to fail where otherwise it wouldn't.

Second, consider, if on the way in it changes ESI or EDI, or according to [1]
/any/ register except eax, ecx and edx, then it needs to save those register
values somewhere, and make a CALL, not a JMP, in order to be able to restore
those values on the way out, because that preservation vis-a-vis calling code
/is/ a requirement of stdcall. It won't do to just push them on the stack prior
to invocation of target. They must be saved somewhere else, for thread safety in
thread local storage. Then you're introducing extreme inefficiency, relative to
this functionality (sitting in the middle of a host of call chains), and also
problems with dynamically loaded DLLs. So in order to do this ungood stuff,
trampling on all registers that could be used to pass arguments size, you have
to do a lot of complicated stuff you'd otherwise not need to do, plus
introducing (relatively speaking) extreme inefficiency, plus, I think, making
your trampoline unusable in certain scenarios relating to DLLs.

Now if that trampling trampoline doesn't qualify as a purely academic objection,
far out of any practical consideration, then I don't know what is.


>> Anyways, good code in general has a property called *robustness*.
>> It doesn't avail itself of pecularities of a given small context of
>> usage, but is designed to work in general, assuming the least instead
>> of the most about its environment.
>
> Oh, the irony. It is _you_ who in effect is saying "gee, here is a
> register I am going to steal for calling variadic functions with my new
> convention, I bet no one knew that register existed, so I'll just assume
> no existing code would break if I misrepresent my new convention as
> stdcall".

Again, it's impossible to break existing code.

All that can happen in the worst case is that some existing very badly designed
code (see right above for details about why it necessarily must be specially
tailored to be problematic, unless created by moron) can't be used to handle the
new stdcall variadic functions, which is no problem.

In best case, which except for academic objection (see above) is expected, there
are not even such restriction on usage of the new functions, and in guaranteed
case, trading a little efficiency (e.g. push instead of register), there are not
even any potential problems.


Cheers, & hth.,

- Alf

Notes:
[1] <url: http://msdn.microsoft.com/en-us/library/cc267758.aspx>

Ben Voigt [C++ MVP]

unread,
Jul 22, 2008, 10:06:03 AM7/22/08
to
[snip]

> And second, your article contained four individual fallacies which I
> pointed out individually, no one individual pointing-out-of-fallacy
> relying on others.

Well, by assigning neatly numbered points to each sequential piece of the
argument, I think we've shown the argument is valid, you contest one of the
premises. Which is quite different from having four fallacies, but that is
the advantage of laying things out so neatly.

I'm now going to quote you out-of-order but I think not out of context.

[snip]

>> QED, by contradiction of consequence #3 with point #1.
>
> Point #1 does not hold. :-)
>

Ok, now that we've gotten that squared away, we can address only Point #1 in
the future.


[snip]


>
>
>> I will present my argument in a simpler form to make it easier to
>> respond to.
>
> Good, thank you.
>
>
>> Premise #1 -- An calling convention implements stdcall iff it in
>> binary compatible with every other conformant implementation.
>
> This either places very strong constraints on "binary compatible", or
> is simply wrong.
>
> In general it's simply wrong -- but see comment at end of this
> section below.
> Although I don't consider mangled names to be part of stdcall
> convention, first example that binary compatibility doesn't hold for
> that. g++ produces mangled name "__Z3food@8" for "void __stdcall foo(
> double ) {}", whereas MSVC produces mangled name "?foo@@YGXN@Z". I'm
> sorry for addressing since you don't bring it explicitly up, but the
> vagueness of "binary compatible" means it could mean just about
> anything, so, if you need even more forceful arg about that, consider
> e.g. not-mangled names of MessageBox routines in user32.dll.
> Arguing against myself, it's not unreasonable to consider /C/ mangled
> names as part of stdcall convention.

What if we restricted ourselves to "only calls made through a raw function
pointer (i.e. not functor, not pointer-to-member)"? Then we get rid of the
whole "locating-the-function" issue and focus on "calling-the-function".

>
> Arguing back against myself, hey, that's a neat notion, but only for
> a subset of cases; e.g. it falls flat on its face when compared to
> the Windows API reality, where names are not mangled that way, but
> are certainly stdcall.
> Then, if hopefully that's a complete enough exposition of the
> mangling as part of convention or not (yes in some cases, no in
> general, e.g. for Windows API), example of different machine code for
> same __stdcall routine.
> #include <iostream>
>
> struct Blah { int x; Blah(): x(666) {} };
>
> Blah __stdcall foo() { Blah x; return x; } // This
> routine.
> int main()
> {
> Blah const b = foo();
> std::cout << b.x << std::endl;
> }
>
> Here g++ (default options) returns the result in register EAX,

Looks like g++ has chosen sizeof(x) <= sizeof (EAX), if I might be so bold
as to illegally mix sizeof with a register name -- it's not valid C nor
assembler, do we all understand what this pseudo-code expression means?

>
> .def __Z3foov@0; .scl 2; .type 32; .endef
> __Z3foov@0:
> push ebp
> mov ebp, esp
> mov eax, 666
> pop ebp
> ret
>
> while MSVC (default options) employs RVO where the caller must supply
> the address where the result should be placed,

Looks like VC++ has chosen sizeof(x) > sizeof (EAX), we might ask ourselves
why, but in that case the binary compatibility problem stems from having
arguments with different layouts, not compiler-dependent choices in how to
implement the calling convention. Or something, because it looks like size
= 4 for the return value.

>
> _x$ = -4 ; size = 4
> ___$ReturnUdt$ = 8 ; size = 4
> ?foo@@YG?AUBlah@@XZ PROC NEAR ; foo
> push ebp
> mov ebp, esp
> push ecx
> lea ecx, DWORD PTR _x$[ebp]
> call ??0Blah@@QAE@XZ ; Blah::Blah

RVO is not used, it would have eliminated the argument x and directly
constructed the return value at [ebp + __$ReturnUdt$__].

But it constructs in the local variable space (< ebp).

> mov eax, DWORD PTR ___$ReturnUdt$[ebp]
> mov ecx, DWORD PTR _x$[ebp]

And here it copies the variable x to the return code whose address is in the
parameter space (> ebp). No RVO.

> mov DWORD PTR [eax], ecx
> mov eax, DWORD PTR ___$ReturnUdt$[ebp]

And it also returns the value in eax, like g++ does.

> mov esp, ebp
> pop ebp
> ret 4
>
> I hope you understand this, that in the g++ case above the caller
> pushes nothing, calls foo() and gets a result back in register eax,
> while in the MSVC case the caller must push the caller's result
> storage address before calling foo, i.e., that the generated machine
> code for the two tools *is not binary compatible* in spite of both
> tools generating perfectly acceptable stdcall.

So it appears. But struct Blah is not POD in C++03 due to the existance of
a constructor, so it's not particularly relevant to any discussion on
variadic functions (I learned my lesson about passing non-PODs to variadic
functions -- the standard forbids it!).

>
> I can elaborate if you want, but the point is, evidenced by facts
> above, your premise #1 is simply wrong in general -- although, I
> hasten the emphasise, it *is* an important consideration for a large
> subset of cases.

I agree. We do need to define what it means to be stdcall better.
Otherwise we could say that fastcall and cdecl functions are stdcall, which
we know isn't true in general.

Alf P. Steinbach

unread,
Jul 22, 2008, 12:19:38 PM7/22/08
to
* Ben Voigt [C++ MVP]:
> [snip]
>
>> And second, your article contained four individual fallacies which I
>> pointed out individually, no one individual pointing-out-of-fallacy
>> relying on others.
>
> Well, by assigning neatly numbered points to each sequential piece of the
> argument, I think we've shown the argument is valid, you contest one of the
> premises. Which is quite different from having four fallacies, but that is
> the advantage of laying things out so neatly.

This statement could easily be misinterpreted by some reader other than me, due
to the quoting technique you employed here.

The four fallacies were mentioned in the context of your previous article, call
it X, not the one you're talking about above, and we're talking about here, call
it Y, with numbered points.

In article Y I though the reasoning was fine, but premise #1 didn't hold.

Not a bad idea. However, at least as I've learned to use these terms, and how
they're used by e.g. Microsoft, "stdcall" is in practice somewhat relative to
context. Within a given language implementation it includes language
implementation specific name mangling. Then there is the simplified C name
mangling as common denominator between language implementations. Then there is
the raw machine code calling convention level, the call-via-pointer level.


>> Arguing back against myself, hey, that's a neat notion, but only for
>> a subset of cases; e.g. it falls flat on its face when compared to
>> the Windows API reality, where names are not mangled that way, but
>> are certainly stdcall.
>> Then, if hopefully that's a complete enough exposition of the
>> mangling as part of convention or not (yes in some cases, no in
>> general, e.g. for Windows API), example of different machine code for
>> same __stdcall routine.
>> #include <iostream>
>>
>> struct Blah { int x; Blah(): x(666) {} };
>>
>> Blah __stdcall foo() { Blah x; return x; } // This
>> routine.
>> int main()
>> {
>> Blah const b = foo();
>> std::cout << b.x << std::endl;
>> }
>>
>> Here g++ (default options) returns the result in register EAX,
>
> Looks like g++ has chosen sizeof(x) <= sizeof (EAX), if I might be so bold
> as to illegally mix sizeof with a register name -- it's not valid C nor
> assembler, do we all understand what this pseudo-code expression means?

This is OK. But stdcall as /documented/ by Microsoft requires using register
pair if necessary, as I recall. So even if Visual C++ padded the struct up to 8
bytes (it did not) then that should not force it to do anything but return
result via register.


>> .def __Z3foov@0; .scl 2; .type 32; .endef
>> __Z3foov@0:
>> push ebp
>> mov ebp, esp
>> mov eax, 666
>> pop ebp
>> ret
>>
>> while MSVC (default options) employs RVO where the caller must supply
>> the address where the result should be placed,
>
> Looks like VC++ has chosen sizeof(x) > sizeof (EAX),

Nope (see below), but it hardly matters. :-)


> we might ask ourselves
> why, but in that case the binary compatibility problem stems from having
> arguments with different layouts, not compiler-dependent choices in how to
> implement the calling convention. Or something, because it looks like size
> = 4 for the return value.
>
>> _x$ = -4 ; size = 4

As you can see here MSVC sets aside 4 bytes for the struct. That fits in EAX,
even without removing any end-padding (and there isn't any). If it didn't fit
then MSVC should, without RVO influencing the decision, choose a register pair.


>> ___$ReturnUdt$ = 8 ; size = 4
>> ?foo@@YG?AUBlah@@XZ PROC NEAR ; foo
>> push ebp
>> mov ebp, esp
>> push ecx
>> lea ecx, DWORD PTR _x$[ebp]
>> call ??0Blah@@QAE@XZ ; Blah::Blah
>
> RVO is not used, it would have eliminated the argument x and directly
> constructed the return value at [ebp + __$ReturnUdt$__].

Right, in this concrete case. I think I formulated that badly. The possibility
of RVO means that MSVC's simplistic implementation always (at least as far as I
know, I don't have that documented) passes that storage pointer, for non-POD.


> But it constructs in the local variable space (< ebp).
>
>> mov eax, DWORD PTR ___$ReturnUdt$[ebp]
>> mov ecx, DWORD PTR _x$[ebp]
>
> And here it copies the variable x to the return code whose address is in the
> parameter space (> ebp). No RVO.
>
>> mov DWORD PTR [eax], ecx
>> mov eax, DWORD PTR ___$ReturnUdt$[ebp]
>
> And it also returns the value in eax, like g++ does.
>
>> mov esp, ebp
>> pop ebp
>> ret 4
>>
>> I hope you understand this, that in the g++ case above the caller
>> pushes nothing, calls foo() and gets a result back in register eax,
>> while in the MSVC case the caller must push the caller's result
>> storage address before calling foo, i.e., that the generated machine
>> code for the two tools *is not binary compatible* in spite of both
>> tools generating perfectly acceptable stdcall.
>
> So it appears. But struct Blah is not POD in C++03 due to the existance of
> a constructor, so it's not particularly relevant to any discussion on
> variadic functions (I learned my lesson about passing non-PODs to variadic
> functions -- the standard forbids it!).

On the contrary, it's very relevant.

One issue was whether you need absolute binary compatibility in order to have
stdcall, that was the issue of your premise #1.

I guess nobody here, well, almost nobody, would deny that the above two binary
incompatible routines are both stdcall, and from the same source code.

So whatever lets this work in the context of the imagined hypothetical problems
with binary compatibility, also lets stdcall variadic functions work even if
they're implemented in wildly different ways by different tools.

Or put another way, if stdcall variadic functions can't work and be stdcall when
they're implemented differently by different tools, then neither can the above.
The above does work no problem. Hence.

Another issue was whether a hidden argument can be passed by compiler, and still
have stdcall.

It is above.


>> I can elaborate if you want, but the point is, evidenced by facts
>> above, your premise #1 is simply wrong in general -- although, I
>> hasten the emphasise, it *is* an important consideration for a large
>> subset of cases.
>
> I agree. We do need to define what it means to be stdcall better.
> Otherwise we could say that fastcall and cdecl functions are stdcall, which
> we know isn't true in general.

What would be Good would IMHO be

* An authoritative definition of what registers must be preserved by function
(two different sets are stated by Microsoft, natural choice would be the
smallest one, which I think is also the latest, in order not to break code).

* An authoritative definition of what registers can be clobbered by e.g.
a trampoline function such as Liviu suggested (natural choice would be
none, then let non-conforming code, if any, fight it out in market).

* An authoritative definition of how non-static member function's this pointer
is passed, and perhaps ditto for args total size for variadic function.

* An authoritative definition of for which argument types the stdcall
convention requires a certain way of passing them / returning them. This is
the essential thing saying that if you have routine with such restricted
signature, you really know how to call it at machine code level,
irrespective of tool.

* And what it allows (tool-dependent) for other argument types.

and not the least, as I think you're saying above and as I've lamented on (is
that correct English?) several times in this thread,

* A *clear separation* of what's specific to Windows API stdcall ("general
stdcall"), what's specific to MSVC "C", and what's specific to MSVC "C++".

plus of course linking from all the five or six or more current pages to one
common page.

It might seem a little bit late for this, as we're passing over into x64 world.

But I think x86 32-bit code will continue to be produced and maintained for a
few years still.


Cheers,

- Alf

Liviu

unread,
Jul 22, 2008, 1:19:35 PM7/22/08
to
"Alf P. Steinbach" <al...@start.no> wrote
>* Liviu:
>> "Alf P. Steinbach" <al...@start.no> wrote
>>> * Liviu:
>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>> * Liviu:
>>>>>> "Alf P. Steinbach" <al...@start.no> wrote
>>>>>>> * Liviu:
>>
>> "/before/" or not is meaningless here. The given code is generic, and
>> works for any stdcall or cdecl functions, present or future.
>
> You've demonstrated that your code does not work generically for all
> stdcall functions, as I understand the term

That's your problem right there ;-)

> -- because it mangles register values willy-nilly. ;-)

For registers not marked as reserved for use by stdcall.

>>> Why the heck would you reserve the EXX (any E...) register in
>>> general?
>>
>> Umm... That's straight from your "for example in a register" answer
>> to the question of how variadic functions would receive the number of
>> actual arguments. Seems quite obvious that you cannot use a register
>> unless you somehow reserve it, so that related code knows that it's
>> off limits to them.
>
> If you choose to use a register for passing the size info to variadic
> routines, all that's needed is to document that for this language
> implementation, a stdcall variadic routine uses this register, say,
> ESI; you don't need to reserve the register in general.

You miss the point, or feigning to. Point is that whatever register you
think you found which is not used by stdcall, and so decided to
appropriate it for "variadic stdcall", someone equally bright might have
found it longtime ago, and be already using the same register for
different purposes _within_ the specs of stdcall. Stealing the register
now for "variadic stdcall" would break the other person's old code at
the binary level. Thus, again, (stdcall + ESI) != stdcall.

Say, as an example, that you used ESI to pass the size info in bytes of
all variadic "..." arguments pushed onto the stack. Then say that I am
proposing a similar, just slightly different, variadic convention using
ESI to pass the size info in DWORDs. By your argument, we would both
have the right to claim that what we have is "stdcall" - though it's
clear the two conventions are not binary compatible. Yet, for some
reason, you insist that yours and yours only be called stdcall.

>> That's exactly
>> what would make the "new stdcall" not binary compatible with stdcall.
>
> It is possible but only barely possible that you think that two source
> code identical routines compiled as __stdcall with different compilers
> or compiler option sets must be binary compatible.

Yes, they should be able to call each other at the machine code level.
That's exactly what a calling convention like stdcall exists for. How
else do you think mixed language programming (let alone mixed compiler)
could possibly work?

> That's not the case with g++ and msvc, which are the main ones I use,

Compile "int __stdcall foo(int bar) { return bar + 1; } with both and
compare the assembly listings. Look quite similar, don't they?

I know / read that there are issues due to function name decoration and
linker technicalities. None of which has anything to do with stdcall per
se, however. And even those issues must not be insurmontable, since
mingw seems to be happily using msvcrt.

> Please repeat some 580 times or so: it's impossible to break binary
> compatibility with code that does not yet exist.

Wow. If the windows developers reasoned the same (wrong) way, we'd all
be in big trouble by now ;-) After all, what they do all the time is
write code bound by published specs which insure binary compatibility
with future yet-to-be-written applications.

Cheers,
Liviu


Ben Voigt [C++ MVP]

unread,
Jul 22, 2008, 2:32:39 PM7/22/08
to
Alf P. Steinbach wrote:
> * Ben Voigt [C++ MVP]:
>> [snip]
>>
>>> And second, your article contained four individual fallacies which I
>>> pointed out individually, no one individual pointing-out-of-fallacy
>>> relying on others.
>>
>> Well, by assigning neatly numbered points to each sequential piece
>> of the argument, I think we've shown the argument is valid, you
>> contest one of the premises. Which is quite different from having
>> four fallacies, but that is the advantage of laying things out so
>> neatly.
>
> This statement could easily be misinterpreted by some reader other
> than me, due to the quoting technique you employed here.
>
> The four fallacies were mentioned in the context of your previous
> article, call it X, not the one you're talking about above, and we're
> talking about here, call it Y, with numbered points.
>
> In article Y I though the reasoning was fine, but premise #1 didn't
> hold.

Well, both my posts used the same argument, although my presentation wasn't
so clear the first time. Yet the first conclusion still holds -- it's not
possible for any variadic stdcall to guarantee compatibility with existing
stdcall code. You have however, made a very convincing argument that
compatibility with other stdcall code isn't a fundamental requirement, as
supported by your evidence of the Microsoft compiler generating machine code
that doesn't follow the documented rules and isn't compatible with other
code (which I checked by compiling your example with VC++ 2005 SP1, the
/EHsc option was needed but it came up with exactly what you posted -- I
didn't doubt that it would, but I wanted to inspect the call site which you
hadn't posted).

Now that we've pinned each other down and forced each other to use precise
terms and no hand-waving, I find myself in total agreement with you (at
least on your conclusions, not whether a new variadic calling convention is
needed):

That "being stdcall" is too weak a statement to be testable, you've proven
quite clearly.

Also, as you so succinctly wrote:

>
> What would be Good would IMHO be
>
> * An authoritative definition of what registers must be preserved
> by function (two different sets are stated by Microsoft, natural
> choice would be the smallest one, which I think is also the
> latest, in order not to break code).
> * An authoritative definition of what registers can be clobbered by
> e.g. a trampoline function such as Liviu suggested (natural
> choice would be none, then let non-conforming code, if any, fight
> it out in market).
> * An authoritative definition of how non-static member function's
> this pointer is passed, and perhaps ditto for args total size for
> variadic function.
> * An authoritative definition of for which argument types the
> stdcall convention requires a certain way of passing them /
> returning them. This is the essential thing saying that if you
> have routine with such restricted signature, you really know how
> to call it at machine code level, irrespective of tool.
>
> * And what it allows (tool-dependent) for other argument types.
>
> and not the least, as I think you're saying above and as I've
> lamented on (is that correct English?) several times in this thread,
>
> * A *clear separation* of what's specific to Windows API stdcall
> ("general stdcall"), what's specific to MSVC "C", and what's
> specific to MSVC "C++".

I agree. I think for that last, what we need is at least a rule to identify
function parameter types/return type that *will* follow the documented "C"
rules without any hidden parameters.

I think MS is gonna have one hell of a hard time implementing C++0x rules
for standard layout types (while your Blah struct isn't POD under C++03 or
C++0x, it is standard-layout in C++0x) wrt calling conventions unless they
totally give up on backward compatibility. But name mangling and such have
always been subject to change between compiler versions. I suspect if we
try a VC++ compiler in a couple years, it will produce the same code as g++,
no hidden argument with an address for the return value.

It is loading more messages.
0 new messages