System V AMD64 struct type parameters calling conventions

799 views Asked by At

I am reading about the System V AMD64 calling conventions. It has a section where it classifies parameters of functions in classes, some of which are INTEGER, SSE, SSEUP, NO_CLASS, MEMORY etc..

I have trouble understanding how it classifies aggregate types i.e. structs, unions and arrays. Here are the rules that are followed for the classification of those.


Rules:

  1. If the size of an object is larger than eight eightbytes, or it contains unaligned fields, it has class MEMORY.

  2. 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).

  3. If the size of the aggregate exceeds a single eightbyte, each is classified separately. Each eightbyte gets initialized to class NO_CLASS.

  4. Each field of an object is classified recursively so that always two fields are considered. The resulting class is calculated according to the classes of the fields in the eightbyte:

    • (a) If both classes are equal, this is the resulting class.
    • (b) If one of the classes is NO_CLASS, the resulting class is the other class.
    • (c) If one of the classes is MEMORY, the result is the MEMORY class.
    • (d) If one of the classes is INTEGER, the result is the INTEGER.
    • (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, MEMORY is used as class.
    • (f) Otherwise class SSE is used.
  5. Then a post merger cleanup is done:

    • (a) If one of the classes is MEMORY, the whole argument is passed in memory.
    • (b) If X87UP is not preceded by X87, the whole argument is passed in memory.
    • (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument is passed in memory.
    • (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE.

My problem is with understanding rules 3,4,5 and how they apply along each other.

Here is an example, how would these rules apply here on B?

struct A{
    int a;
    long b;
    char c;
};

struct B{
    char a;
    A b;
    short* c;
};

My take on it is:

First, sizeof(B) < 64 and the fields I assume are laid out properly aligned. Therefore rule 1 does not apply here.

Second, rule 2 also does not apply.

Here, however is the confusing part for me, at this moment, the way I understand it, rules 3,4 can both be applied. So there are two paths:

  1. The first path is to follow rule 3. Rule 3 can be applied since sizeof(B) > 8, so it tells me to break the B object into all the eightbytes that it is being composed of. (Again assuming proper alignent) I see 5 eightbytes two for B.a and B.c and 3 for the each field of A. Now the rule says that each eightbyte is being initialized to NO_CLASS. But what would now be the next step to find out the class of B? Do I consult rule 5?

  2. On the other hand the other path is to follow and apply rule 4. The way I understand rule 4 says, first find out the class of each field of your struct and through the subrules of rule 4 deduct the class of the whole struct. But the bit strange part in the rule for me here is when it says

    according to the classes of the fields in the eightbyte

    What does it mean with the eightbyte there? Does this mean that rule 4 applies only on eightbyte objects? Doesn't it apply for any kind objects-struct? Is it probably applied on the resulting "objects" of rule 3?

Finally where rule 5 comes into play?

0

There are 0 answers