Ответ: что угодно.
Если вы используете регистры сохранения вызываемого абонента, компилятор сохранит их для вас (если вы делаете пометите их как помеченные).
Если вы используете временные регистры (a.k.a. caller-save), то компилятор будет вынужден сохранить их, если вы сделаете вызов функции. Имейте в виду, что компилятор также предпочитает использовать их для других переменных, поэтому, если вы используете те, которые сохраняют вызывающую функцию, ему придется использовать функцию сохранения вызываемой переменной для других вещей, так что в конечном итоге это может сильно отличаться.
В конце концов, если вы делаете тяжелые вычисления, то сохранение нескольких регистров в стеке перед началом не будет большой проблемой.
Некоторые регистры содержат важные значения (например, указатель стека), которые вы не должны перезаписывать. Другие, такие как указатель таблицы GOT, менее важны, и компилятор восстановит значение, когда вы закончите (просто убедитесь, что оно вам не нужно во время процесса.
На самом деле вам не нужно самим разбираться: компилятор может выбирать регистры для вас:
int a, b, c;
asm volatile ("whatever" : "=&w" (a), "=&w" (b), "=&w" (c));
Переменные не нужны, но им должны быть назначены регистры, поэтому они эффективно резервируют регистр для чего угодно. &
обозначает «ранний клоббер», что означает, что они не могут совместно использовать тот же регистр, что и входной регистр (не то, что мой пример показывает их).