Does the standard promise to remove unnecessary parameters?

60 views
Skip to first unread message

Nemo Yu

unread,
Jan 27, 2016, 9:22:58 PM1/27/16
to std-dis...@isocpp.org
 e.g.

struct A{}; struct B{};

void foo(int i, A);
void foo(int i, B);

when I call foo(0, A()), will the argument A be pushed into the stack or not? I mean, by the standard, will it be forced or suggested to ignore the unnecessary parameters/arguments?

I give another example.

void foo(int, A, int);
void foo(int, B, int);

How is it going? I don't think in this case the argument A can be ignored, or the sequence of arguments will be unknown for the foo function, if the caller do ignore A.

(Excuse me I just too lazy to check to the standard....)

Nicol Bolas

unread,
Jan 27, 2016, 9:31:56 PM1/27/16
to ISO C++ Standard - Discussion
There is no such thing as "unnecessary parameters/arguments" as far as the standard is concerned. Nor does that concept make sense.

The fact that a declaration does not name a parameter does not, and never has, meant that the parameter will certainly never be used. After all, the function definition may name it and use it just fine. Alternatively (and more likely), the declaration will name all the parameters, but the definition will only name the ones it uses. How could the call cite (which does not necessarily have access to the definition) know that a variable is unused?

Tony V E

unread,
Jan 27, 2016, 9:57:26 PM1/27/16
to Nemo Yu
In the standard there is no stack. Parameters are passed by magic. 

A compiler, if it can tell the param is unused, can ignore the param (ie not put it on the stack). Or whatever else it wants, as long as the effect is the same. This is the 'as if' rule. As long as the observable results are the same, the compiler can do whatever.

Sent from my BlackBerry portable Babbage Device
From: Nemo Yu
Sent: Wednesday, January 27, 2016 9:22 PM
Subject: [std-discussion] Does the standard promise to remove unnecessary parameters?

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

Thiago Macieira

unread,
Jan 27, 2016, 10:05:07 PM1/27/16
to std-dis...@isocpp.org
On Wednesday 27 January 2016 21:57:23 Tony V E wrote:
> In the standard there is no stack. Parameters are passed by magic.
>
> A compiler, if it can tell the param is unused, can ignore the param (ie not
> put it on the stack). Or whatever else it wants, as long as the effect is
> the same. This is the 'as if' rule. As long as the observable results are
> the same, the compiler can do whatever.

Another ABI trick is that empty structs can occupy zero slots in the
parameter-passing medium.

But not necessarily. This is entirely up to the ABI. If you want to influence
the portable ABI used by GCC and Clang, join the cxx-a...@codesourcery.com
mailing list.

Here's what it does:

struct A{}; struct B{};
void foo(int, A, int);
void f() { foo(1, A(), 2); }

When compiled with GCC:

movl $2, %esi
movl $1, %edi
pushq $0
call _Z3fooi1Ai

The trivial struct occupies space in the stack. Note that Clang doesn't do it:

movl $1, %edi
movl $2, %esi
jmp _Z3fooi1Ai # TAILCALL

ICC agrees with GCC that there's a parameter passed on the stack, but instead
of pushing a zero, it copies garbage.

movl $1, %edi
movl $2, %esi
movq %rsp, %rdx
movb 16(%rsp), %al
movb %al, (%rdx)
call _Z3fooi1Ai

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Nemo Yu

unread,
Jan 27, 2016, 10:09:04 PM1/27/16
to std-dis...@isocpp.org
Thank you for replying. 
 
 This is the 'as if' rule.

I don't totally understand the words "as if" in English (forgive me not a native speaker), may you explain it somehow?

 How could the call cite (which does not necessarily have access to the definition) know that a variable is unused?
  
  As long as the observable results are the same, the compiler can do whatever.

I think the caller and called function can have an agreement: callers never push a tail argument marked as "unnecessary" and called functions never pop it, then the stack is balanced, as long as the ignored one is tail but not middle. A special case is inline functions, I think it can always ignore the unnecessary argument for compile period inline(I don't know if it is for link period inline).

However, as what Tony said:

In the standard there is no stack. Parameters are passed by magic. 

It does make sense...... 

Nemo Yu

unread,
Jan 27, 2016, 10:20:16 PM1/27/16
to std-dis...@isocpp.org
This is entirely up to the ABI. If you want to influence
the portable ABI used by GCC and Clang

What if foo is a template function or inline function:

template<typename T> inline void foo(int, T) {} // T is never used.

If T is not optimized, T does occupy space and waste performance.

Tony V E

unread,
Jan 27, 2016, 10:25:22 PM1/27/16
to Nemo Yu
'as if' == imagine that it happened

For example, I start a job at a new company. ‎It has a dress code. You must wear a suit and tie. But also, when putting on your pants, you must put on the right leg first, then the left. 

So I show up every day in a suit and tie, and if asked, I say I put the right leg on first. 

But did I? They can't tell, so just imagine that I did - the end result is the same.

You mentioned inlining. The fact that a compiler is allowed to inline at all is because you can't tell (without looking at the assembly) whether it was inlined or not, the result is the same (besides speed and code side). Was the parameter passed on the stack? Maybe the param didn't exist at all after inlining. 
 

Sent from my BlackBerry portable Babbage Device
From: Nemo Yu
Sent: Wednesday, January 27, 2016 10:09 PM
Subject: Re: [std-discussion] Does the standard promise to remove unnecessary parameters?

Thiago Macieira

unread,
Jan 27, 2016, 11:47:58 PM1/27/16
to std-dis...@isocpp.org
On Thursday 28 January 2016 11:20:15 Nemo Yu wrote:
> > This is entirely up to the ABI. If you want to influence
> > the portable ABI used by GCC and Clang
>
> What if foo is a template function or inline function:
>
> template<typename T> inline void foo(int, T) {} // T is never used.

The fact that it's a template is irrelevant.

The fact that it's inline is. If a function gets inlined, then there's no
call.

> If T is not optimized, T does occupy space and waste performance.

Yes. If that happens with your compiler, file a bug.

FrankHB1989

unread,
Jan 28, 2016, 12:46:29 AM1/28/16
to ISO C++ Standard - Discussion


在 2016年1月28日星期四 UTC+8上午11:09:04,math0写道:
Thank you for replying. 
 
 This is the 'as if' rule.

I don't totally understand the words "as if" in English (forgive me not a native speaker), may you explain it somehow?

The rule is normatively defined in the standard, although the word "as-if" is in informal text.

 How could the call cite (which does not necessarily have access to the definition) know that a variable is unused?
  
  As long as the observable results are the same, the compiler can do whatever.

I think the caller and called function can have an agreement: callers never push a tail argument marked as "unnecessary" and called functions never pop it, then the stack is balanced, as long as the ignored one is tail but not middle. A special case is inline functions, I think it can always ignore the unnecessary argument for compile period inline(I don't know if it is for link period inline).

This may be confined by ABI, if any.
 
However, as what Tony said:

In the standard there is no stack. Parameters are passed by magic. 

It does make sense...... 

However, it is out of the scope of C++. ISO C++ does not formally define the structure of the activity records. The only standardized relevant term is "stack unwinding", but it is also not necessarily bound to the call stack in a typical implementation.
 

Nemo Yu

unread,
Jan 28, 2016, 1:01:47 AM1/28/16
to std-dis...@isocpp.org
> If T is not optimized, T does occupy space and waste performance.

Yes. If that happens with your compiler, file a bug.

How about big long functions

struct A{}; struct B{};
void foo(int, A) { /* It is too long to be an inline function */ }
void foo(int, B) { /* It is too long to be an inline function */ }

If I want the second parameter to be optimized, have I better to:

struct A{}; struct B{};
void foo_A(int) { /* It is too long to be an inline function */ }
void foo_B(int) { /* It is too long to be an inline function */ }
inline void foo(int i, A) { foo_A(i); }
inline void foo(int i, B) { foo_B(i); }

Is it a better way for performance?

The fact that it's a template is irrelevant.

The fact that it's inline is. If a function gets inlined, then there's no
call.

I mentioned template because templates are always written in header files, so compilers have much liberties to decide the way it converted to source, such like optimizing the unnecessary parameters. When we directly write source files, it is more about the ABI. In case of templates, I think it is more about the compiler optimization.
 

--

Nathan Ernst

unread,
Jan 28, 2016, 1:38:44 AM1/28/16
to std-dis...@isocpp.org
The size of a function *may* be a heuristic to the compiler on whether or not a function can/should be inlined, but it is not required to be so, nor is there necessarily any real human-usable guideline for "small-enough". And even then, the same compiler may produce different results depending on targeting x86, x64, or an AVR chip (a compiler might more aggressively inline in a 64-bit address space where it has to be more size-conscious in an 8 or 16-bit address space, for instance).

Likewise, the "inline" keyword does not force a function to be inlined, but is a suggestion to the compiler that it can/should be done (there are other implications to the "inline" keyword, but I'm reserving my commentary to a function marked as "inline" actually being inlined).. The compiler can, and often does, disregard this (and sometimes does both - i.e. in the context of a shared/dynamic library). MSVC warns when a function marked inline is not actually inlined. (Try writing a simple program that initializes a vector with 10 ints and cout's them with the /Wall option, instead of the default /W3 level, - you'll likely get lots of warnings. For instance, in the following trivial program, using VS2015 (community edition, update 1, targeting x86) with /Wall on, I get 24 warnings - all in regards to functions *not* being inlined. At the default warning level, no warnings.

#include <vector>
#include <iostream>

int main()
{
std::vector<int> vec(10);

for(auto i: vec)
std::cout << i << std::endl;

std::cout << "done" << std::endl;

    return 0;
}

Function size is just a heuristic. It's not a guarantee. Sure, smaller functions are probably more easily inlined. And, "inline" is really just a suggestion. If you really want to know what is going on, take a look at the assembly output of your (optimized) compiler's output.

Viacheslav Usov

unread,
Jan 28, 2016, 6:30:30 AM1/28/16
to std-dis...@isocpp.org
On Thu, Jan 28, 2016 at 3:22 AM, Nemo Yu <mzer...@gmail.com> wrote:
 e.g.

struct A{}; struct B{};

void foo(int i, A);
void foo(int i, B);

when I call foo(0, A()), will the argument A be pushed into the stack or not? I mean, by the standard, will it be forced or suggested to ignore the unnecessary parameters/arguments?

I give another example.

void foo(int, A, int);
void foo(int, B, int);

How is it going? I don't think in this case the argument A can be ignored, or the sequence of arguments will be unknown for the foo function, if the caller do ignore A.

Pushing on stack is only part of the issue. Besides, on modern 64-bit architectures, nothing would be pushed on stack in any case (given the example code).

What is more important is side effects, as have been mentioned by others. In this particular case the second unnamed parameter must be copy-constructed and eventually destructed. Your example has no ctor/dtor defined, so all that will likely be optimized away, but when such optimization is not possible, the cost of unnamed parameters could be significant.

Cheers,
V.

Mikhail Maltsev

unread,
Jan 28, 2016, 6:49:04 AM1/28/16
to std-dis...@isocpp.org
On 01/28/2016 06:04 AM, Thiago Macieira wrote:
> On Wednesday 27 January 2016 21:57:23 Tony V E wrote:
>> In the standard there is no stack. Parameters are passed by magic.
>>
>> A compiler, if it can tell the param is unused, can ignore the param (ie not
>> put it on the stack). Or whatever else it wants, as long as the effect is
>> the same. This is the 'as if' rule. As long as the observable results are
>> the same, the compiler can do whatever.
>
> Another ABI trick is that empty structs can occupy zero slots in the
> parameter-passing medium.
>
> But not necessarily. This is entirely up to the ABI. If you want to influence
> the portable ABI used by GCC and Clang, join the cxx-a...@codesourcery.com
> mailing list.
>
> Here's what it does:
>
> struct A{}; struct B{};
> void foo(int, A, int);
> void f() { foo(1, A(), 2); }
>
> When compiled with GCC:
>
> movl $2, %esi
> movl $1, %edi
> pushq $0
> call _Z3fooi1Ai
>
It's a bug. Empty structs should not occupy stack slots. It is fixed in
GCC 6: https://gcc.gnu.org/ml/gcc-patches/2015-12/msg01444.html

--
Regards,
Mikhail Maltsev

Mikhail Maltsev

unread,
Jan 28, 2016, 7:16:06 AM1/28/16
to std-dis...@isocpp.org
On 01/28/2016 09:01 AM, Nemo Yu wrote:
> I mentioned* template *because templates are always written in header
> files, so compilers have much liberties to decide the way it converted to
> source, such like optimizing the unnecessary parameters. When
> we directly write source files, it is more about the ABI. In case of
> templates, I think it is more about the compiler optimization.
That's not true. You may declare template in a header and define (and
explicitly instantiate) it in one translation unit. All other
translation units will not see the definition. When template
instantiations are compiled, the compiler cannot know, whether other
translation units contain the definition or just declaration.

OTOH, if the function is defined in an anonymous namespace, has static
linkage and the function does not have it's address taken, or if you
compile the program with LTO, all call sites are known and the compiler
can remove unnecessary parameters. GCC performs such optimization.

--
Regards,
Mikhail Maltsev

Nemo Yu

unread,
Jan 28, 2016, 8:07:47 AM1/28/16
to std-dis...@isocpp.org
You may declare template in a header and define (and
explicitly instantiate) it in one translation unit. 

As mentioned in the standard, you cannot do that, because template definition must be in header files, so for a header file you cannot exclude it when calls.

Mikhail Maltsev

unread,
Jan 28, 2016, 8:28:14 AM1/28/16
to std-dis...@isocpp.org
On 01/28/2016 04:07 PM, Nemo Yu wrote:
>>
>> You may declare template in a header and define (and
>> explicitly instantiate) it in one translation unit.
>
>
> As mentioned in the standard, you cannot do that, because template
> definition must be in header files, so for a header file you cannot exclude
> it when calls.
The standard does not use the term 'header' except for standard library
headers. Template definition does not need to be present in the
translation unit if you use explicit instantiation declaration (see
[temp.explicit]).

--
Regards,
Mikhail Maltsev

Nemo Yu

unread,
Jan 28, 2016, 10:04:47 AM1/28/16
to std-dis...@isocpp.org
> it when calls.
The standard does not use the term 'header' except for standard library
headers. Template definition does not need to be present in the
translation unit if you use explicit instantiation declaration (see
[temp.explicit]).

OK, I remember now, the explicit initialization does an exception.


--
Regards,
    Mikhail Maltsev

Tony V E

unread,
Jan 28, 2016, 1:37:35 PM1/28/16
to Nemo Yu
> Is it a better way for performance?

Worrying about whether an unused variable‎ is pushed or not is probably not a better way for performance. 

Measuring is a better way for performance. 
Changing your algorithm is a better way for performance. Ie going from O(N^2) vs O(NlogN)

Not allocating. 

There are lots of ways to better performance before worrying about unused variables. 



 ‎

Sent from my BlackBerry portable Babbage Device
From: Nemo Yu
Sent: Thursday, January 28, 2016 1:01 AM
Subject: Re: [std-discussion] Does the standard promise to remove unnecessary parameters?
Reply all
Reply to author
Forward
0 new messages