Гарантируют ли компиляторы C две восьмибайтовые полевые структуры как INTEGER на SysV x64? - PullRequest
3 голосов
/ 06 июня 2019

В частности, в контексте SysV x86-64 ABI

Если у меня есть структура только с двумя полями, например:

typedef struct {
    void *foo;
    void *bar;
} foobar_t;

И я передаю его функции с таким определением:

foobar_t example_function(foobar_t example_param);

ABI, кажется, говорит, что каждое восьмибайтовое поле должно быть передано функции как INTEGER, следовательно, rdi == foo и rsi == bar. Аналогично, при возврате мы должны иметь возможность использовать rax и rdx, так как нам не нужен указатель памяти в rdi. Если example_function тривиально определяется как:

foobar_t example_function(foobar_t example_param) {
    return example_param;
}

Допустимая реализация сборки, игнорирующая пролог и эпилог, будет:

example_function:
   mov rax, rdi
   mov rdx, rsi
   ret

Вероятно, психически неполноценный компилятор может заполнить структуру заполнением NO_CLASS и сделать эту сборку недействительной. Мне интересно, записано ли где-нибудь, что структура с только двумя восьмибайтовыми полями должна обрабатываться таким образом.

Более широкий контекст моего вопроса заключается в том, что я пишу простой переключатель задач C11 для своего собственного назидания. Я основываю это в основном на boost.context и , именно так Boost проходит двухполевые структуры вокруг . Я хочу знать, является ли это кошерным при любых обстоятельствах, или если повышение немного обманывает.

Ответы [ 2 ]

3 голосов
/ 06 июня 2019

ABI, кажется, говорит, что каждое восьмибайтовое поле должно быть передано как INTEGER функции, поэтому rdi == foo и rsi == bar.

Согласовано, для "global"функции доступны из нескольких блоков компиляции , структура аргумента разбита на куски по восемь байт, первая полностью заполнена foo, а вторая полностью заполнена bar.Они классифицируются как INTEGER и поэтому передаются в% rdi и% rsi соответственно.

Аналогично, при возврате мы должны иметь возможность использовать rax и rdx, так как мы не делаемнужен указатель памяти в rdi.

Я не слежу за вашей точкой зрения о% rdi, но я согласен, что члены возвращаемого значения возвращаются в% rax и% rdx.

Действительная реализация сборки, игнорирующая пролог и эпилог, была бы: [...]

Согласовано.

Возможно, мысленнонесовершенный компилятор может заполнить структуру заполнением NO_CLASS и сделать эту сборку недействительной.Мне интересно, записано ли где-нибудь, что структура с только двумя восьмибайтовыми полями должна обрабатываться таким образом.

Компилятор, который создает код, соответствующий SysV x86-64ABI будет использовать уже обсуждаемые регистры для передачи аргумента и возврата возвращаемого значения.Такой компилятор, конечно, не обязан реализовывать тело функции именно так, как вы описываете, но я не вижу вашей озабоченности.Да, эти детали записаны.Хотя конкретный случай, который вы представляете, явно не описан в спецификации ABI, которую вы связали, все поведение, рассмотренное выше, следует из этой спецификации.Это точка этого.

Компилятор, который генерирует код (для глобальной функции), который ведет себя по-другому, не является умственно несовершенным, он не соответствует .

Более широкий контекст моего вопроса заключается в том, что я пишу простой переключатель задач C11 для своего собственного назидания.Я основываю это в основном на boost.context, и именно так boost передает два поля.Я хочу знать, является ли он кошерным при любых обстоятельствах или буст обманывает.

Мне потребовалось бы больше анализа, чем я готов потратить, чтобы точно определить, что Boost делает в коде.Вы указываете на.Обратите внимание, что это , а не , что вы представляете в своем example_function.Но разумно предположить, что Boost по крайней мере пытается реализовать свои вызовы функций в соответствии с ABI.

3 голосов
/ 06 июня 2019

Компиляторы, согласовывающие структуру структуры и то, как они передаются по значению в качестве аргументов функции, являются ключом частями ABI. В противном случае они не могли бы вызывать функции друг друга.

Рукописный asm не отличается от генерируемого компилятором asm; это не обязательно должно происходить из одной и той же версии того же компилятора, чтобы нормально взаимодействовать. Вот почему стабильные и правильные ABI такие большие проблемы.

Совместимость с рукописным asm довольно схожа с совместимостью с машинным кодом, который был скомпилирован давным-давно и годами находился в двоичной разделяемой библиотеке. Если это было правильно тогда, это правильно сейчас. Если структуры не изменились в исходном коде вновь скомпилированный код может вызываться и вызываться существующими инструкциями.


Если компилятор не соответствует стандартному, как написано, он не работает.

Или, может быть, точнее, если он не соответствует gcc, он сломан. И если стандартная формулировка не описывает, что делает gcc / clang / ICC, то стандартный документ не работает.

Если у вас был компилятор для x86-64 System V, который передает структуру 2x void* любым способом, кроме двух регистров, этот компилятор не работает, не ваш рукописный asm .

(Предполагается, что не так много более ранних аргументов, которые используют регистры передачи аргументов, прежде чем мы перейдем к struct arg.)

...