Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
184 views
in Technique[技术] by (71.8m points)

C++ calling convention for passing big objects on Linux/x86-84

I'm trying to understand the overhead of pass object by value as a function parameter in C++/Linux/x86-64 platform.

The experimental code I used for the exploration is posted below and on godbolt.org: https://godbolt.org/z/r9Yfv4

Assume the function is unary. What I observed is:

  1. If the parameter object is a 8 bytes in size, it will be put in RDI.
  2. If the parameter is 16 bytes in size (contain two sub-object each 8 bytes), the two sub-objects will be put in to RDI and RSI.
  3. If the parameter is bigger than 16 bytes, it will be passed via stack.

I only consider integral types and pointers and the composition types of these basic types. I know passing floats/doubles is different.

The size of std::function is 32 bytes (GCC/Linux implementation, long + long + pointer + pointer = 32 bytes.). So passing std::function by value should look like pass struct Person4 defined in my code. But the output assembly shows that pass std::function is very different from pass struct Person3. It looks like std::function is passed via a pointer, am I right? Why there is such a difference?

#include <functional>

struct Person0 {
  long name;
};

long GetName(Person0 p) {
  return p.name;
}

struct Person1 {
  long name;
  long age;
};

long GetName(Person1 p) {
  return p.name;
}

struct Person2 {
  long name;
  long age;
  long height;
};

long GetName(Person2 p) {
  return p.name;
}

struct Person3 {
  long name;
  long age;
  long height;
  long weight;
};

long GetName(Person3 p) {
  return p.name + sizeof(p);
}

long Invoke(std::function<long(long)> f) {
  return f(20) + sizeof(f);
}


int main() {
  Person3 p;
  p.name = 13;
  p.age = 23;
  p.height = 33;
  p.weight = 43;
  long n = GetName(p);

  std::function<long(long)> ff;
  Invoke(ff);
  return 0;
}
question from:https://stackoverflow.com/questions/65838379/c-calling-convention-for-passing-big-objects-on-linux-x86-84

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The document that you want to read is the System V ABI for x86-64, in particular, section 3.2.3 ?Paramater Passing?

Structs that are > 32 bytes, go always on the stack. For structs that are <= 32 bytes, there is some logic going on:

Paramater Passing Rules

The post merger cleanup says that given that the size is greater than 2 eighbytes (16 bytes), and the first parameter is not an SSE, or any other parameter is not SSEUP, the whole aggregate is classified as MEMORY (stack).

Regarding the use of std::function, there is one last rule that might explain it:

  1. If a C++ object has either a non-trivial copy constructor or a non-trivial destructor, it is passed by invisible reference (the object is replaced in the parameter list by a pointer that has class INTEGER)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...