Я понимаю соглашения о вызовах для передачи 32-битных и 64-битных целых чисел (и указателей), чисел с плавающей запятой и удвоений для 64-битного кода для Microsoft и System V AMD64 ABI. Но мне непонятно, какие существуют соглашения о вызовах для составных типов данных.
Чтобы было понятнее, каковы соглашения о вызовах для передачи структур, классов и объединений по значению в функциях с внешней связью (т. е. не static inline
функций)? Меня особенно интересуют простые структуры, такие как
typedef struct doublefloat { float hi; float lo; } doublefloat;
typedef struct doubledouble { double hi; double lo; } doubledouble;
typedef struct int128 { int64_t hi; int64_t lo; } int128;
doublefloat foof(float a, float b);
doubledouble food(double a, double b);
float foo3(doubledouble a, doubledouble b);
int128 fooi(int64_t a, int64_t b);
Вот что я наблюдал в GCC (с -O3)
foof
возвращаетhi
иlo
, упакованные в первые 64 битаXMM0
.food
возвращаетhi
иlo
вXMM0
иXMM1
.foo3
проходитhi
иlo
изa
иb
вXMM0
,XMM1
,XMM2
иXMM3
.fooi
возвращаетhi
иlo
в rda и rdx
Агнер Фог описывает детали (которые согласуются с наблюдениями) для каждого компилятора на http://www.agner.org/optimize/calling_conventions.pdf
См. Таблица 6. Методы передачи объектов структуры, класса и объединения и Таблица 7. Методы возврата объектов структуры, класса и объединения.
Для 64-битного кода его таблицы разделены на Windows и Linux/BSD/Mac, а не на компилятор, поэтому для меня это означает, что существует некий стандарт для составных типов данных. Правильно ли это или передача и возврат составного типа данных потенциально определяются каждым компилятором или каждой версией компилятора (т. е. могут измениться в следующей версии).
Обратите внимание, что я понимаю, что на практике во многих из этих случаев, вероятно, лучше всего использовать static inline
. Также обратите внимание, что хотя в C нет классов, меня все равно интересует, как структуры и объединения передаются по значению в C, а не только в C++, поэтому я включил тег C.