Re: calling convention stdcalll and cdecl call
"Alf P. Steinbach" <alfps@start.no> wrote in message
news:Ta-dnXgco-Stfh7VnZ2dnUVZ_v7inZ2d@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).
--
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