Позвольте мне начать с повторения того, что я сказал выше: я не говорю на PP C asm, и у меня нет PP C для запуска этого кода. Поэтому, хотя я считаю, что обычно , это направление, в котором вы должны действовать, не воспринимайте этот код как евангелие.
Далее, причина, по которой Джестер и я предложили использовать локальный регистр Переменные в том, что это приводит к лучшему (и, возможно, более читабельному / поддерживаемому) коду. Причиной этого является то, что эта строка в g cc документах :
G CC не анализирует сами инструкции ассемблера и не знает, что они означают или даже являются ли они допустимыми для ввода ассемблера.
Имея это в виду, что происходит, когда вы используете код, подобный описанному выше, и вызываете подпрограмму с кодом, подобным:
int parameters_array[maximum_syscall_parameter_count] = {1, 2, 3, 4, 5, 6, 7};
long a = execute_system_call_with_arguments(9, parameters_array);
компилятор не знает, что произойдет внутри этого блока asm, он должен записать все в память, которую блок asm затем считывает из памяти в регистры. При использовании кода, подобного приведенному ниже, компилятор может быть достаточно умен, чтобы пропустить когда-либо выделение памяти и загрузить регистры напрямую. Это может быть еще более полезно, если вы вызываете execute_system_call_with_arguments
более одного раза с (по существу) одинаковыми параметрами.
constexpr auto maximum_syscall_parameter_count = 7;
long execute_system_call_with_arguments(const int value, const int parameters_array[maximum_syscall_parameter_count]) {
int return_value_buffer[2];
register int foo0 asm("0") = value;
register int foo1 asm("3") = parameters_array[0];
register int foo2 asm("4") = parameters_array[1];
register int foo3 asm("5") = parameters_array[2];
register int foo4 asm("6") = parameters_array[3];
register int foo5 asm("7") = parameters_array[4];
register int foo6 asm("8") = parameters_array[5];
register int foo7 asm("9") = parameters_array[6];
// Execute the syscall
asm volatile ("sc"
: "+r"(foo3), "+r"(foo4)
: "r"(foo0), "r"(foo1), "r"(foo2), "r"(foo5), "r"(foo6), "r"(foo7)
);
return_value_buffer[0] = foo3;
return_value_buffer[1] = foo4;
return *(long *) &return_value_buffer;
}
При вызове с приведенным выше примером выдает:
.L.main:
li 0,9
li 3,1
li 4,2
li 5,3
li 6,4
li 7,5
li 8,6
li 9,7
sc
extsw 3,6
blr
Сохранение максимально возможного количества кода вне шаблона asm (ограничения считаются «внешними») позволяет оптимизаторам g cc делать все виды полезных вещей.
Несколько других моментов :
- Если какой-либо из элементов в
parameters_array
является (или может быть) указателем, вам необходимо добавить саблобер памяти . Это гарантирует, что любые значения, которые могут быть сохранены в регистрах, сбрасываются в память перед выполнением инструкции asm. Добавление clobber памяти, если он не нужен (может) замедлить выполнение с помощью пары инструкций. Отказ от этого при необходимости может привести к чтению неверных данных. - Если
sc
изменяет любые регистры, которые здесь не перечислены, вы должны перечислить их как всплывающие подсказки. И если какие-либо регистры, перечисленные здесь (кроме foo3 и foo4), изменяются, вы должны также сделать их input + output (sc
помещает код возврата в foo0?). Даже если вы «не используете их» после вызова asm, если они меняются, вы ДОЛЖНЫ сообщить об этом компилятору. Поскольку g cc docs явно предупреждает :
Не изменяйте содержимое операндов только для ввода (кроме входов, связанных с выходами). Компилятор предполагает, что при выходе из оператора asm эти операнды содержат те же значения, которые они имели до выполнения оператора.
Если не учитывать это предупреждение, код может нормально работать в один прекрасный день, затем внезапно вызывает странные сбои в некоторый момент после (иногда значительно позже) блока asm. Это «работает, а потом вдруг нет» - одна из причин, по которой я предлагаю вам не использовать встроенный asm , но если вы должны (что Вы делаете это, если вам нужно позвонить sc
напрямую), оставьте его как можно меньше.
Я немного обманул, изменив
maximum_syscall_parameter_count
на 7. По-видимому, g cc Godbolt не оптимизирует этот код также с большим количеством параметров. Если это необходимо, могут быть способы обойти это, но вам понадобится лучший эксперт по PP C, чем я.