Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is an unnamed parameter actually passed during a function call?

template <typename TAG>
fn(int left, TAG, int right)
{
}

fn(0, some_type_tag(), 1);
/* or */
fn(0,int(), 1); // where the primitive, int, is not empty.

EDIT: There are two perspectives to this question.

  1. Function declaration vs definition. The declaration might not name the parameter, but the declaration might do.This isn't the perspective of interest.
  2. The template perspect, spectifically in meta-programming. The parameter In question is a tag used to pull out a meta-structure out of a trait. This is why the parameter is unnamed, I only care about the compile-time information - the type of the tag.

/EDIT

My tags are generally empty-structs, however in some parts of my code they are typedefs of primitive types.So, I'm interested to know if modern compilers will actually pass a parameter. This has two aspects.

  1. Sizing the stack, taking into account the size of the unnamed parameter type.
  2. Actually constructing the stack with the passed value.

Lets keep it to gcc 4.5 and msvc 2008+

like image 537
Hassan Syed Avatar asked May 20 '11 12:05

Hassan Syed


People also ask

Which can be passed as an argument to a function?

Arguments are passed by value; that is, when a function is called, the parameter receives a copy of the argument's value, not its address. This rule applies to all scalar values, structures, and unions passed as arguments. Modifying a parameter does not modify the corresponding argument passed by the function call.

Where are the arguments passed to a function stored?

Parameter values to functions are stored on the stack as well, pushed immediately before the return address. Everything what lives on the stack (local variables, parameters etc.)

How arguments are passed to functions in C?

Arguments in C and C++ language are copied to the program stack at run time, where they are read by the function. These arguments can either be values in their own right, or they can be pointers to areas of memory that contain the data being passed. Passing a pointer is also known as passing a value by reference.

How many parameters can a function have?

When you call a function in JavaScript, you can pass in any number of arguments, regardless of what the function declaration specifies. There is no function parameter limit. In the above function, if we pass any number of arguments, the result is always the same because it will take the first two parameters only.


2 Answers

C++ has separate translation. Since the parameter can be named in the declaration but not in the function definition and vice versa, there's generally no way whether the compiler knows whether it's safe to omit the function argument. When it's all in the same translation unit, everything could be inlined and the argument name is entirely irrelevant to optimization.

[Added]

The seperate translation may not matter to this specific case, but a compiler builder that would add such an optimization must care. They're not going to put in such optimizations if it breaks perfectly valid code.

As for templates, it's necessary that the type of a template function is equal to the type of a non-template function, else it's impossible to take its address and assign it to a function pointer. Again, you have to take into account seperate translation. Just because you don't take the address of foo<int> in this TU doesn't mean you won't in another.

like image 117
MSalters Avatar answered Oct 12 '22 14:10

MSalters


It's quite an interesting question actually.

First of all, note that we are in an imperative language, meaning that when you ask for something (even useless, such as constructing an unused object) then the compiler need to comply unless it can come up with an equivalent form. Basically, it could elide the parameter if it could prove that doing so would not change the meaning of the program.

When you write a function call, two things may happen (in the end):

  • either it is inlined
  • or a call is actually emitted

If it is inlined, then no parameter is passed, which effectively means that unused objects can be removed (and not even built) if the compiler can prove that the constructors and destructors involved do not perform any significant work. It works well for tags structures.

When a call is emitted, it is emitted with a specific calling convention. Each compiler has its own set of calling conventions which specify how to pass the various arguments (this pointer, etc...), generally trying to take advantage of the available registers.

Since only the declaration of the function is used to determine the calling convention (separate compilation model), then it is necessary to actually pass the object...

However, if we are talking about an empty structure, with no method and no state, then this is just some uninitialized memory. It should not cost much, but it does require stack space (at least, reserving it).

Demo using the llvm tryout:

struct tag {};

inline int useless(int i, tag) { return i; }

void use(tag);

int main() {
  use(tag());
  return useless(0, tag());
}

Gives:

%struct.tag = type <{ i8 }>

define i32 @main() {
entry:
  ; allocate space on the stack for `tag`
  %0 = alloca %struct.tag, align 8                ; <%struct.tag*> [#uses=2]

  ; get %0 address
  %1 = getelementptr inbounds %struct.tag* %0, i64 0, i32 0 ; <i8*> [#uses=1]

  ; 0 initialize the space used for %0
  store i8 0, i8* %1, align 8

  ; call the use function and pass %0 by value
  call void @_Z3use3tag(%struct.tag* byval %0)
  ret i32 0
}

declare void @_Z3use3tag(%struct.tag* byval)

Note:

  • how the call to useless was removed, and no argument is build for it
  • how to call to use cannot be removed, and therefore space is allocated for the temporary (I hope that the new versions don't 0-initialize the memory)
like image 29
Matthieu M. Avatar answered Oct 12 '22 15:10

Matthieu M.