Почему Golang сохраняет только %rsp, %rip и %rbp (никаких других регистров, сохраняемых вызываемым пользователем) при переключении контекста?

В общих реализациях переключения контекста пользовательского потока (например, setjmp/longjmp и способ function return) мы сохраняем и восстанавливаем регистры, сохраненные вызываемым пользователем, но golang сохраняет и восстанавливает только %rsp, %rip и %rbp в gobuf.

Возьмите x86_64 в качестве примера, golang сохранит контекст горутины с помощью среды выполнения .gosave и восстановить контекст горутины с помощью среды выполнения .гого.

Так почему же Golang делает это именно так?


person Lime    schedule 11.02.2020    source источник
comment
IIRC использует аргументы стека даже на x86-64, а не на x86-64 System V ABI. Являются ли это единственными регистрами, сохраняемыми вызовами, в пользовательском соглашении о вызовах Go (которое используется для любых обычных функций)? Или у него есть какой-то способ указать, что вызов runtime.gosave уничтожит все остальные регистры, заставив вызывающую сторону сохранить их в этом случае. Я не использую go, поэтому не могу легко проверить; надеюсь, кто-то, кто знает ABI и дизайн Go, сможет легче ответить на ваш вопрос.   -  person Peter Cordes    schedule 11.02.2020
comment
Это может быть историческим с первого компилятора. Обратите внимание, что нет необходимости сохранять/восстанавливать другие регистры.   -  person Volker    schedule 11.02.2020
comment
@PeterCordes Спасибо. Это имеет отношение к golang ABI. Как сказал Ян Well, sort of. In the current ports there are no callee-saved registers other than the frame pointer. groups.google.com/forum/m/ #!topic/golang-dev/HDLMMYQv7Ak   -  person Lime    schedule 11.02.2020
comment
Фу, какое противное соглашение о вызовах. Компилятор встраивается очень агрессивно, чтобы убедиться, что внутри нет узких циклов с небольшим вызовом функции?   -  person Peter Cordes    schedule 11.02.2020


Ответы (1)


Судя по всему, GoLang по-прежнему использует неэффективное соглашение о вызовах, при котором единственными сохраняемыми вызовами (т. е. энергонезависимыми) регистрами являются RSP и RBP.

Вызов runtime.gosave выглядит для компилятора как любой другой вызов функции (т. е. он в конечном итоге возвращается после выполнения некоторых действий и не изменяет ничего выше своего собственного фрейма стека). Как и любой другой вызов функции, вызывающая сторона должна предполагать, что он уничтожает все затираемые вызовом (изменчивые) регистры (все, кроме RSP и RBP). Таким образом, любые значения, которые он хочет сохранить после вызова, должны быть перенесены в слоты стека (или в другую область памяти, где они принадлежат).

По той же причине C setjmp должен сохранять только регистры, сохраняемые вызовом. И функции переключения контекста ядра такие же.


В этом сообщении о группах Google от 2017 года говорится, что так оно и есть. соглашение / ABI работает, и из связанного кода похоже, что он до сих пор не улучшен.

Соглашение о вызовах Go также неэффективно передает все аргументы в стеке, в отличие от x86-64 System V ABI, который передает первые 6 целочисленных аргументов (и первые 8 FP) в регистрах.

person Peter Cordes    schedule 11.02.2020