Собственно, для хранения промежуточных значений выражений, используется регистровый стэк. Просто он состоит всего из трех регистров (eax, ecx, edx) или (rax, rcx, rdx). Когда свободные регистры заканчиваются, регистр, который находится на дне регистрового стэка заносится в программный стэк, а остальные сдвигаются на одну позицию и компилятор запоминает, что один операнд находится в программном стэке. Есть кое-какие нюансы, но в общем, идея понятна.
Но так было не всегда. Сначала было шесть регистров: eax, ecx, edx, ebx, esi, edi. Но регистры в x86 неравноценны: eax, ecx, edx, ebx включают в себя 8- и 16-битные части (al, cl... ax, cx...), a esi и edi единые и неделимые. Некоторые операции требуют именно 8- или 16-битный регистр в качестве операнда. Это конечно не проблема, но всё же, это обстоятельство несколько усложняет компилятор. Я попробовал отключить регистры esi и edi -- скорость выполнения тестовых программ не изменилась. Регистр ebx по соглашению stdcall требуется сохранять перед использованием и восстанавливать после. Это неудобно, и я отключил еще и ebx. Скорость уменьшилась на 5%. Ради интереса, я отключил еще и edx, оставив только eax и ecx. И только после этого производительность заметно просела. Я сделал вывод, что оптимальное число регистров для хранения промежуточных значений выражений - 4 (можно 3). Поэтому, я решил не усложнять компилятор, использовать регистровый стэк из трех регистров. Остальные регистры зарезервированы и теоретически могут быть использованы под хранение переменных (но с этим пока сложно).
соотв. тема (собственный рабочий компилятор для Оберона)
Выход под FASM.
Отредактировано MihalNik (2018-12-21 12:40:39)