Вызов функции C ++ в системные вызовы RIS C -V - PullRequest
1 голос
/ 10 апреля 2020

У меня странная ситуация, которая, кажется, работает хорошо для меня, но мне нужно знать, как сделать это лучше или как жить этим.

Я использую C ++ как скомпилированный язык сценариев для игровой движок. Системный вызов RIS C -V ABI аналогичен соглашению о вызове функции C, за исключением того, что вместо 8-го целочисленного аргумента или аргумента-указателя для номера системного вызова используется A7. Да, вы знаете, куда это идет. Вот:

extern "C" long syscall_enter(...);

template <typename... Args>
inline long syscall(long syscall_n, Args&&... args)
{
    asm volatile ("li a7, %0" : : "i"(syscall_n));
    return syscall_enter(std::forward<Args>(args)...);
}

В то время как syscall_enter - это просто символ в .text с инструкцией syscall и ret. Возвращаемое значение системного вызова также совпадает с регистром обычного возврата функции.

000103f0 <syscall_enter>:
syscall_enter():
   103f0:       00000073                ecall
   103f4:       00008067                ret

Перед этим мне пришлось создать более 20 функций, чтобы охватить все различные способы выполнения системных вызовов с целыми числами и указателями с барьер компилятора, и когда я хотел добавить функцию, которая принимала бы значения с плавающей точкой, он сказал бы, что вызов был неоднозначным, поскольку целые числа и числа с плавающей точкой могут быть преобразованы назад и вперед. Итак, я мог бы либо начать добавлять уникальные имена в функции, либо просто решить эту проблему лучше. Это было честно раздражать и ставить амортизатор на отличный опыт. Мне очень нравится возможность использовать C ++ на «обеих сторонах».

Инструкции, сгенерированные компилятором, кажутся нормальными. Это JAL и JALR syscall_enter, что нормально. Компилятор кажется немного сбитым с толку, но я не возражаю против одной дополнительной инструкции.

   10204:       1f500793                li      a5,501
   10208:       00078893                mv      a7,a5
   1020c:       00000513                li      a0,0
   10210:       1e0000ef                jal     ra,103f0 <syscall_enter>

Как и центральная камера на позиции:

   100d4:       19600793                li      a5,406
   100d8:       00078893                mv      a7,a5
   100dc:       000127b7                lui     a5,0x12
   100e0:       4207b587                fld     fa1,1056(a5) # 12420 <_exit+0x2308>
   100e4:       22b58553                fmv.d   fa0,fa1
   100e8:       010000ef                jal     ra,100f8 <syscall_enter>

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

Теперь, есть ли еще лучший способ? Я не мог придумать лучшего способа загрузить a7 числом, а затем заставить компилятор установить вызов функции без фактического вызова функции. Я думал об использовании параметра шаблона для номера системного вызова, но в остальном я не уверен. Может быть, мы можем ограничить количество аргументов до 7? Это не будет правильно, когда есть целочисленные аргументы и аргументы с плавающей точкой, но это нормально. Структуры, хранящиеся в стеке, легко передать.

После некоторого тестирования я решил использовать это:

extern "C" long syscall_enter(...);

template <typename... Args>
inline long syscall(long syscall_n, Args&&... args)
{
    // This will prevent some cases of too many arguments,
    // but not a mix of float and integral arguments.
    static_assert(sizeof...(args) < 8, "There is a system call limit of 8 integer arguments");
    // The memory clobbering prevents reordering of a7
    asm volatile ("li a7, %0" : : "i"(syscall_n) : "a7", "memory");
    return syscall_enter(std::forward<Args>(args)...);
    asm volatile("" : : : "memory");
}

Должно быть достаточно. Нет необходимости для спама функции syscall. Проверка для подсчета аргументов не является оптимальной, поскольку она должна только предотвращать использование 8-го интегрального регистра (что означает подсчет интегральных, указательных и ссылочных параметров). Но это предотвратит некоторые случаи.

Ответы [ 2 ]

2 голосов
/ 10 апреля 2020

Есть две проблемы с этим.

Во-первых, вы не говорите компилятору, что используете a7, поэтому он может попытаться поместить туда что-то еще, что приведет к некорректному коду. Вам необходимо добавить a7 в список clobbers asm:

asm volatile ("mv a7, %0" : : "r"(syscall_n) : "a7");

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

1 голос
/ 11 апреля 2020

Это функция, которую я использую сейчас. Большое спасибо @PeterCordes за помощь.

extern "C" long syscall_enter(...);

template <typename... Args>
inline long apicall(long syscall_n, Args&&... args)
{
    // This will prevent some cases of too many arguments,
    // but not a mix of float and integral arguments.
    static_assert(sizeof...(args) < 8, "There is a system call limit of 8 integer arguments");
    // The memory clobbering prevents reordering of a7
    asm volatile ("li a7, %0" : : "i"(syscall_n) : "a7", "memory");
    return syscall_enter(std::forward<Args>(args)...);
    asm volatile("" : : : "memory");
}

Это хорошо работает для меня. Опять же, основная причина, по которой следует избегать решения syscall-function-spam, заключается в том, что если у вас есть 2 функции, одна из которых принимает интегральный аргумент, а другая - аргумент с плавающей запятой, то вызов функции будет неоднозначным, и теперь вам нужно начать думать о том, какую функцию вызывать. Я протестировал это решение с использованием сочетания с плавающей точкой и целочисленных аргументов, и оно работает так, как должно. Один недостаток состоит в том, что он помещает аргументы с плавающей запятой в 64-битные регистры, поэтому он будет немного медленнее во время системного вызова.

Опять же, было решение C ++!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...