рассмотрим следующую функцию, которая не будет встроена, и примем x86 в качестве платформы:
void doSomething(int & in){
//do something
}
во-первых, я не уверен, что такой сценарий произойдет, но, поскольку я думаю, что это возможно, я спрошу, так что ЕСЛИ в любом вызывающем объекте эта функция вызывается, то передаваемый аргумент находится точно в верхней части фрейма стека вызывающего, так что в доступ к вызываемой функции к этому через регистр ebp (после того, как вызываемый объект переместил содержимое esp в ebp) на языке ассемблера возможен, вы предлагаете вообще игнорировать объявление параметра для функции и использовать ассемблер для доступа к нашим аргументам в этом исключительном случае или просто оставить определение функции как есть и оставить компилятору делать то, что он делает? поскольку я нигде не читал, этот компилятор рассматривал бы такой исключительный случай как фактор для соглашения о вызовах, и я думаю, что он просто сгенерирует код для передачи указателя на аргумент в фрейм стека вызываемого или в один из регистров
Прежде всего, это так легко сломать — например, вы получаете другую версию компилятора, которая генерирует код по-разному. Или вы меняете функции оптимизации. Не берите в голову ситуацию, когда вам вдруг нужно использовать doSomething
в другом месте, и тогда это не будет работать, потому что переменная больше не находится на вершине стека.
Во-вторых, если предположить, что код внутри функции достаточно короткий, весьма вероятно, что компилятор встроит функцию, так что вы вообще ничего не «потеряете».
В-третьих, единственный аргумент в современных компиляторах, как правило, в любом случае передается в регистр, поэтому при включенной оптимизации это не дает никаких преимуществ.
Если вы действительно считаете, что в этом есть какая-то полезная выгода, и компилятор не встроит или не оптимизирует код иным образом [вы смотрели на сгенерированный код?], Тогда попробуйте использовать forceinline
или же always_inline
или как там это называется в вашем компиляторе (у большинства компиляторов есть такая опция). Если это не сработает, используйте макрос, чтобы вставить его вручную. Или просто переместите код туда, где он называется «copy-n-paste».
Ваша заметкапередаваемый аргумент находится точно в верхней части фрейма стека вызывающего, так что в вызываемой функции доступ к нему осуществляется через регистр ebp«содержит фактическое недоразумение.
Это из-за следующих вещей:
push
перед вызовом в стек call
в функции. Это обычно не так; даже на 32-битной платформе x86 существуют соглашения о вызовах, не основанных на стеке (например, Windows fastcall
или GNU GCC, используемые в 32-битном ядре Linux). Если таковые используются, аргумент не будет найден в верхней части стека, а скорее … в любом регистре, который используется для хранения первого аргумента.
Но даже если у вас есть передача параметров на основе стека … все же:
call
инструкция толкает обратный адрес на вершину стека, так что, когда первая инструкция функции достигла этого пути, выполняется, ESP
будет указывать не на первый аргумент этой функции, а на обратный адрес.EBP
регистры, сохраненные вызываемым пользователем (сохраняются через вызовы функций) и не инициализируются архитектурой от вашего имени — необходимо, чтобы сгенерированный код эксплицитно настроить это. Поэтому функция, которая хочет использовать его (даже если только как указатель кадра), обязана сохранить его где-то перед использованием. Это означает, что нормальный пролог буду иметь push EBP; mov EBP, ESP
(ты не можешь только делать MOV EBP, ESP
потому что это было бы затирать вызывающий EBP
который недействителен / что вы не можете сделать). Поэтому, если вы хотите сослаться на первый аргумент функции, вам нужно [ EBP + 8 ]
не [ EBP ]
,call
который был использован для достижения функции, нажав адрес возврата) находится в [ ESP + 4 ]
не [ ESP ]
,Я надеюсь, что это проясняет немного.
Я согласен с другими постерами, что прояснение вопроса поможет, чего именно вы хотите достичь и почему, по вашему мнению, язык ассемблера может быть полезен здесь.
Нет, я не буду. Соглашения о вызовах могут различаться (между x86 и x86_64); Параметры могут быть помещены в стек или помещены в регистр, и я не уверен, что вы точно знаете, где они будут.
Написание этого на ассемблере, если вы действительно не знаете, что делаете, может привести к неопределенному коду поведения.