Ограничение
A "g"
позволяет компилятору выбирать память или регистр, поэтому, очевидно, вы получите mov mem,mem
, если это произойдет.mov
может иметь максимум 1 операнд памяти.(Как и во всех инструкциях x86, возможен не более одного явного операнда памяти.)
Используйте ограничения "ri"
для входов, которые будут перемещены в место назначения памяти, чтобы разрешить регистр или немедленный, но не память.
Кроме того, вы модифицируете RSP, поэтому вы не можете безопасно использовать операнды источника памяти.Компилятор предполагает, что он может использовать режимы адресации, такие как [rsp+16]
или [rsp-4]
.Таким образом, вы не можете использовать push
вместо mov
.
Вам также необходимо объявить clobbers во всех регистрах call-clobbered, потому что вызов функции сделает это.(Или лучше, возможно, запросить входные данные в этих регистрах, перекрывающих вызовы, чтобы компилятору не приходилось сбрасывать их через сохраненные вызовы регистры, такие как RBX. Но вам нужно сделать эти операнды для чтения / записи или объявить отдельные выходные операнды дляте же регистры, чтобы компилятор знал, что они будут изменены.)
Так что, вероятно, ваш лучший выбор для эффективности - что-то вроде
int ecx, edx, edi, esi; // dummy outputs as clobbers
register int r8 asm("r8d"); // for all the call-clobbered regs in the calling convention
register int r9 asm("r9d");
register int r10 asm("r10d");
register int r11 asm("r11d");
// These are the regs for x86-64 System V.
// **I don't know what Go actually clobbers.**
asm("sub rsp, 0xe0\n\t" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] \n\t"
"push %[fn_str] \n\t"
"call \n\t"
"add rsp, 0xe0 + 3*8 \n\t" // pop red-zone skip space + pushed args
// real output in RAX, and dummy outputs in call-clobbered regs
: "=a"(retval), "=c"(ecx), "=d"(edx), "=D"(edi), "=S"(esi), "=r"(r8), "=r"(r9), "=r"(r10), "=r"(r11)
: [fn_str] "ri" (filename.str), [fn_len] "ri" (filename.len), etc. // inputs can use the same regs as dummy outputs
: "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", // All vector regs are call-clobbered
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
"memory" // if you're passing any pointers (even read-only), or the function accesses any globals,
// best to make this a compiler memory barrier
);
Обратите внимание, что выходные данные не early-clobber, поэтому компилятор может (по своему усмотрению) использовать эти регистры для входных данных, но мы не заставляем его, поэтому компилятор все еще может использовать какой-либо другой регистр или непосредственный.
При дальнейшем обсуждении функции Go не блокируют RBP, поэтому нет причин сохранять / восстанавливать его вручную.Единственная причина, по которой вы, возможно, захотите, заключается в том, что местные жители могут использовать режимы адресации, относящиеся к RBP, и более старый GCC допустил ошибку, объявляя клоббер в RBP при компиляции без -fomit-frame-pointer
.(Я думаю. Или, может быть, я думаю о EBX в 32-битном коде PIC.)
Кроме того, если вы используете ABI System V x86-64, будьте осторожны, что встроенный asm не должензабить красную зону.Компилятор предполагает, что этого не происходит, и нет способа объявить клоббер в красной зоне или даже установить -mno-redzone
для каждой функции.Так что вам, вероятно, нужно sub rsp, 128 + 0xe0
.Или 0xe0
уже содержит достаточно места, чтобы пропустить красную зону, если это не является частью аргументов вызываемого абонента.