too many memory references for `mov' when calling a golang function with C by using inline assembly
I'm trying to call a golang function from my C code. Golang does not use the standard x86_64 calling convention, so I have to resort to implementing the transition myself. As gcc does not want to mix cdecl with the x86_64 convention,
I'm trying to call the function using inline assembly:
void go_func(struct go_String filename, void* key, int error){
void* f_address = (void*)SAVEECDSA;
asm volatile(" sub rsp, 0xe0; tn
mov [rsp+0xe0], rbp; tn
mov [rsp], %0; tn
mov [rsp+0x8], %1; tn
mov [rsp+0x18], %2; tn
call %3; tn
mov rbp, [rsp+0xe0]; tn
add rsp, 0xe0;"
:
: "g"(filename.str), "g"(filename.len), "g"(key), "g"(f_address)
: );
return;
}
Sadly the compiler always throws an error at me that I dont understand:
./code.c:241: Error: too many memory references for `mov'
This corresponds to this line: mov [rsp+0x18], %2; tn If I delete it, the compilation works. I don't understand what my mistake is...
I'm compiling with the -masm=intel flag so I use Intel syntax. Can someone please help me?
c go inline-assembly calling-convention
add a comment |
I'm trying to call a golang function from my C code. Golang does not use the standard x86_64 calling convention, so I have to resort to implementing the transition myself. As gcc does not want to mix cdecl with the x86_64 convention,
I'm trying to call the function using inline assembly:
void go_func(struct go_String filename, void* key, int error){
void* f_address = (void*)SAVEECDSA;
asm volatile(" sub rsp, 0xe0; tn
mov [rsp+0xe0], rbp; tn
mov [rsp], %0; tn
mov [rsp+0x8], %1; tn
mov [rsp+0x18], %2; tn
call %3; tn
mov rbp, [rsp+0xe0]; tn
add rsp, 0xe0;"
:
: "g"(filename.str), "g"(filename.len), "g"(key), "g"(f_address)
: );
return;
}
Sadly the compiler always throws an error at me that I dont understand:
./code.c:241: Error: too many memory references for `mov'
This corresponds to this line: mov [rsp+0x18], %2; tn If I delete it, the compilation works. I don't understand what my mistake is...
I'm compiling with the -masm=intel flag so I use Intel syntax. Can someone please help me?
c go inline-assembly calling-convention
Post your answer as an answer, not an edit to the question. And BTW, you can usepushif you simply change the number that yousubfrom RSP to account for it. Your "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler that the call-clobbered registers are clobbered, and you step on the red-zone because you don'tsubenough stack space to go past it.
– Peter Cordes
Nov 21 '18 at 21:58
add a comment |
I'm trying to call a golang function from my C code. Golang does not use the standard x86_64 calling convention, so I have to resort to implementing the transition myself. As gcc does not want to mix cdecl with the x86_64 convention,
I'm trying to call the function using inline assembly:
void go_func(struct go_String filename, void* key, int error){
void* f_address = (void*)SAVEECDSA;
asm volatile(" sub rsp, 0xe0; tn
mov [rsp+0xe0], rbp; tn
mov [rsp], %0; tn
mov [rsp+0x8], %1; tn
mov [rsp+0x18], %2; tn
call %3; tn
mov rbp, [rsp+0xe0]; tn
add rsp, 0xe0;"
:
: "g"(filename.str), "g"(filename.len), "g"(key), "g"(f_address)
: );
return;
}
Sadly the compiler always throws an error at me that I dont understand:
./code.c:241: Error: too many memory references for `mov'
This corresponds to this line: mov [rsp+0x18], %2; tn If I delete it, the compilation works. I don't understand what my mistake is...
I'm compiling with the -masm=intel flag so I use Intel syntax. Can someone please help me?
c go inline-assembly calling-convention
I'm trying to call a golang function from my C code. Golang does not use the standard x86_64 calling convention, so I have to resort to implementing the transition myself. As gcc does not want to mix cdecl with the x86_64 convention,
I'm trying to call the function using inline assembly:
void go_func(struct go_String filename, void* key, int error){
void* f_address = (void*)SAVEECDSA;
asm volatile(" sub rsp, 0xe0; tn
mov [rsp+0xe0], rbp; tn
mov [rsp], %0; tn
mov [rsp+0x8], %1; tn
mov [rsp+0x18], %2; tn
call %3; tn
mov rbp, [rsp+0xe0]; tn
add rsp, 0xe0;"
:
: "g"(filename.str), "g"(filename.len), "g"(key), "g"(f_address)
: );
return;
}
Sadly the compiler always throws an error at me that I dont understand:
./code.c:241: Error: too many memory references for `mov'
This corresponds to this line: mov [rsp+0x18], %2; tn If I delete it, the compilation works. I don't understand what my mistake is...
I'm compiling with the -masm=intel flag so I use Intel syntax. Can someone please help me?
c go inline-assembly calling-convention
c go inline-assembly calling-convention
edited Nov 22 '18 at 0:03
Michael Petch
25.2k556101
25.2k556101
asked Nov 13 '18 at 18:15
reijinreijin
637
637
Post your answer as an answer, not an edit to the question. And BTW, you can usepushif you simply change the number that yousubfrom RSP to account for it. Your "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler that the call-clobbered registers are clobbered, and you step on the red-zone because you don'tsubenough stack space to go past it.
– Peter Cordes
Nov 21 '18 at 21:58
add a comment |
Post your answer as an answer, not an edit to the question. And BTW, you can usepushif you simply change the number that yousubfrom RSP to account for it. Your "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler that the call-clobbered registers are clobbered, and you step on the red-zone because you don'tsubenough stack space to go past it.
– Peter Cordes
Nov 21 '18 at 21:58
Post your answer as an answer, not an edit to the question. And BTW, you can use
push if you simply change the number that you sub from RSP to account for it. Your "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler that the call-clobbered registers are clobbered, and you step on the red-zone because you don't sub enough stack space to go past it.– Peter Cordes
Nov 21 '18 at 21:58
Post your answer as an answer, not an edit to the question. And BTW, you can use
push if you simply change the number that you sub from RSP to account for it. Your "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler that the call-clobbered registers are clobbered, and you step on the red-zone because you don't sub enough stack space to go past it.– Peter Cordes
Nov 21 '18 at 21:58
add a comment |
2 Answers
2
active
oldest
votes
A "g" constraint allows the compiler to pick memory or register, so obviously you'll end up with mov mem,mem if that happens. mov can have at most 1 memory operand. (Like all x86 instructions, at most one explicit memory operand is possible.)
Use "ri" constraints for the inputs that will be moved to a memory destination, to allow register or immediate but not memory.
Also, you're modifying RSP so you can't safely use memory source operands. The compiler is going to assume it can use addressing modes like [rsp+16] or [rsp-4]. So you can't use push instead of mov.
You also need to declare clobbers on all the call-clobbered registers, because the function call will do that. (Or better, maybe ask for the inputs in those call-clobbered registers so the compiler doesn't have to bounce them through call-preserved regs like RBX. But you need to make those operands read/write or declare separate output operands for the same registers to let the compiler know they'll be modified.)
So probably your best bet for efficiency is something like
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, 0xe0nt" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] nt"
"push %[fn_str] nt"
"call nt"
"add rsp, 0xe0 + 3*8 nt" // 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
);
Notice that the output are not early-clobber, so the compiler can (at its option) use those registers for inputs, but we're not forcing it so the compiler is still free to use some other register or an immediate.
Upon further discussion, Go functions don't clobber RBP, so there's no reason to save/restore it manually. The only reason you might have wanted to is that locals might use RBP-relative addressing modes, and older GCC made it an error to declare a clobber on RBP when compiling without -fomit-frame-pointer. (I think. Or maybe I'm thinking of EBX in 32-bit PIC code.)
Also, if you're using the x86-64 System V ABI, beware that inline asm must not clobber the red-zone. The compiler assumes that doesn't happen and there's no way to declare a clobber on the red zone or even set -mno-redzone on a per-function basis. So you probably need to sub rsp, 128 + 0xe0. Or 0xe0 already includes enough space to skip the red-zone if that's not part of the callee's args.
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
1
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
1
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
1
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it withleaveorpop rbp.
– Peter Cordes
Nov 15 '18 at 14:32
1
@reijin: Then there's no reason to save/restorerbpinside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.
– Peter Cordes
Nov 15 '18 at 14:39
|
show 4 more comments
The original poster added this solution as an edit to their question:
If someone ever finds this, the accepted answer does not help you when you try to call golang code with inline asm! The accepted answer only helps with my initial problem, which helped me to fix the golangcall. Use something like this:**
void* __cdecl go_call(void* func, __int64 p1, __int64 p2, __int64 p3, __int64 p4){
void* ret;
asm volatile(" sub rsp, 0x28; tn
mov [rsp], %[p1]; tn
mov [rsp+0x8], %[p2]; tn
mov [rsp+0x10], %[p3]; tn
mov [rsp+0x18], %[p4]; tn
call %[func_addr]; tn
add rsp, 0x28; "
:
: [p1] "ri"(p1), [p2] "ri"(p2),
[p3] "ri"(p3), [p4] "ri"(p4), [func_addr] "ri"(func)
: );
return ret;
}
1
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.sub rsp, 0x80/ 4x push / call /add rsp, 0x80 + 4*8should do the trick, along with clobbers as shown in my answer. There's no need to usemovto put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)
– Peter Cordes
Nov 21 '18 at 23:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes: Okok, I get it. I prefer themovway, as it doesn't force me to do the additionalsubcall. But I guess it's clearer to usepush. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!
– reijin
Nov 22 '18 at 17:00
@reijin: If you ignore the red-zone, it'smovthat needs asub rsp, offset. Withpushyou can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need onesuband oneadd. The only advantage tomovis that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).
– Peter Cordes
Nov 22 '18 at 17:55
2
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287194%2ftoo-many-memory-references-for-mov-when-calling-a-golang-function-with-c-by-us%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
A "g" constraint allows the compiler to pick memory or register, so obviously you'll end up with mov mem,mem if that happens. mov can have at most 1 memory operand. (Like all x86 instructions, at most one explicit memory operand is possible.)
Use "ri" constraints for the inputs that will be moved to a memory destination, to allow register or immediate but not memory.
Also, you're modifying RSP so you can't safely use memory source operands. The compiler is going to assume it can use addressing modes like [rsp+16] or [rsp-4]. So you can't use push instead of mov.
You also need to declare clobbers on all the call-clobbered registers, because the function call will do that. (Or better, maybe ask for the inputs in those call-clobbered registers so the compiler doesn't have to bounce them through call-preserved regs like RBX. But you need to make those operands read/write or declare separate output operands for the same registers to let the compiler know they'll be modified.)
So probably your best bet for efficiency is something like
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, 0xe0nt" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] nt"
"push %[fn_str] nt"
"call nt"
"add rsp, 0xe0 + 3*8 nt" // 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
);
Notice that the output are not early-clobber, so the compiler can (at its option) use those registers for inputs, but we're not forcing it so the compiler is still free to use some other register or an immediate.
Upon further discussion, Go functions don't clobber RBP, so there's no reason to save/restore it manually. The only reason you might have wanted to is that locals might use RBP-relative addressing modes, and older GCC made it an error to declare a clobber on RBP when compiling without -fomit-frame-pointer. (I think. Or maybe I'm thinking of EBX in 32-bit PIC code.)
Also, if you're using the x86-64 System V ABI, beware that inline asm must not clobber the red-zone. The compiler assumes that doesn't happen and there's no way to declare a clobber on the red zone or even set -mno-redzone on a per-function basis. So you probably need to sub rsp, 128 + 0xe0. Or 0xe0 already includes enough space to skip the red-zone if that's not part of the callee's args.
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
1
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
1
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
1
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it withleaveorpop rbp.
– Peter Cordes
Nov 15 '18 at 14:32
1
@reijin: Then there's no reason to save/restorerbpinside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.
– Peter Cordes
Nov 15 '18 at 14:39
|
show 4 more comments
A "g" constraint allows the compiler to pick memory or register, so obviously you'll end up with mov mem,mem if that happens. mov can have at most 1 memory operand. (Like all x86 instructions, at most one explicit memory operand is possible.)
Use "ri" constraints for the inputs that will be moved to a memory destination, to allow register or immediate but not memory.
Also, you're modifying RSP so you can't safely use memory source operands. The compiler is going to assume it can use addressing modes like [rsp+16] or [rsp-4]. So you can't use push instead of mov.
You also need to declare clobbers on all the call-clobbered registers, because the function call will do that. (Or better, maybe ask for the inputs in those call-clobbered registers so the compiler doesn't have to bounce them through call-preserved regs like RBX. But you need to make those operands read/write or declare separate output operands for the same registers to let the compiler know they'll be modified.)
So probably your best bet for efficiency is something like
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, 0xe0nt" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] nt"
"push %[fn_str] nt"
"call nt"
"add rsp, 0xe0 + 3*8 nt" // 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
);
Notice that the output are not early-clobber, so the compiler can (at its option) use those registers for inputs, but we're not forcing it so the compiler is still free to use some other register or an immediate.
Upon further discussion, Go functions don't clobber RBP, so there's no reason to save/restore it manually. The only reason you might have wanted to is that locals might use RBP-relative addressing modes, and older GCC made it an error to declare a clobber on RBP when compiling without -fomit-frame-pointer. (I think. Or maybe I'm thinking of EBX in 32-bit PIC code.)
Also, if you're using the x86-64 System V ABI, beware that inline asm must not clobber the red-zone. The compiler assumes that doesn't happen and there's no way to declare a clobber on the red zone or even set -mno-redzone on a per-function basis. So you probably need to sub rsp, 128 + 0xe0. Or 0xe0 already includes enough space to skip the red-zone if that's not part of the callee's args.
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
1
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
1
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
1
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it withleaveorpop rbp.
– Peter Cordes
Nov 15 '18 at 14:32
1
@reijin: Then there's no reason to save/restorerbpinside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.
– Peter Cordes
Nov 15 '18 at 14:39
|
show 4 more comments
A "g" constraint allows the compiler to pick memory or register, so obviously you'll end up with mov mem,mem if that happens. mov can have at most 1 memory operand. (Like all x86 instructions, at most one explicit memory operand is possible.)
Use "ri" constraints for the inputs that will be moved to a memory destination, to allow register or immediate but not memory.
Also, you're modifying RSP so you can't safely use memory source operands. The compiler is going to assume it can use addressing modes like [rsp+16] or [rsp-4]. So you can't use push instead of mov.
You also need to declare clobbers on all the call-clobbered registers, because the function call will do that. (Or better, maybe ask for the inputs in those call-clobbered registers so the compiler doesn't have to bounce them through call-preserved regs like RBX. But you need to make those operands read/write or declare separate output operands for the same registers to let the compiler know they'll be modified.)
So probably your best bet for efficiency is something like
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, 0xe0nt" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] nt"
"push %[fn_str] nt"
"call nt"
"add rsp, 0xe0 + 3*8 nt" // 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
);
Notice that the output are not early-clobber, so the compiler can (at its option) use those registers for inputs, but we're not forcing it so the compiler is still free to use some other register or an immediate.
Upon further discussion, Go functions don't clobber RBP, so there's no reason to save/restore it manually. The only reason you might have wanted to is that locals might use RBP-relative addressing modes, and older GCC made it an error to declare a clobber on RBP when compiling without -fomit-frame-pointer. (I think. Or maybe I'm thinking of EBX in 32-bit PIC code.)
Also, if you're using the x86-64 System V ABI, beware that inline asm must not clobber the red-zone. The compiler assumes that doesn't happen and there's no way to declare a clobber on the red zone or even set -mno-redzone on a per-function basis. So you probably need to sub rsp, 128 + 0xe0. Or 0xe0 already includes enough space to skip the red-zone if that's not part of the callee's args.
A "g" constraint allows the compiler to pick memory or register, so obviously you'll end up with mov mem,mem if that happens. mov can have at most 1 memory operand. (Like all x86 instructions, at most one explicit memory operand is possible.)
Use "ri" constraints for the inputs that will be moved to a memory destination, to allow register or immediate but not memory.
Also, you're modifying RSP so you can't safely use memory source operands. The compiler is going to assume it can use addressing modes like [rsp+16] or [rsp-4]. So you can't use push instead of mov.
You also need to declare clobbers on all the call-clobbered registers, because the function call will do that. (Or better, maybe ask for the inputs in those call-clobbered registers so the compiler doesn't have to bounce them through call-preserved regs like RBX. But you need to make those operands read/write or declare separate output operands for the same registers to let the compiler know they'll be modified.)
So probably your best bet for efficiency is something like
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, 0xe0nt" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] nt"
"push %[fn_str] nt"
"call nt"
"add rsp, 0xe0 + 3*8 nt" // 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
);
Notice that the output are not early-clobber, so the compiler can (at its option) use those registers for inputs, but we're not forcing it so the compiler is still free to use some other register or an immediate.
Upon further discussion, Go functions don't clobber RBP, so there's no reason to save/restore it manually. The only reason you might have wanted to is that locals might use RBP-relative addressing modes, and older GCC made it an error to declare a clobber on RBP when compiling without -fomit-frame-pointer. (I think. Or maybe I'm thinking of EBX in 32-bit PIC code.)
Also, if you're using the x86-64 System V ABI, beware that inline asm must not clobber the red-zone. The compiler assumes that doesn't happen and there's no way to declare a clobber on the red zone or even set -mno-redzone on a per-function basis. So you probably need to sub rsp, 128 + 0xe0. Or 0xe0 already includes enough space to skip the red-zone if that's not part of the callee's args.
edited Nov 15 '18 at 14:43
answered Nov 13 '18 at 18:21
Peter CordesPeter Cordes
122k17184312
122k17184312
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
1
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
1
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
1
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it withleaveorpop rbp.
– Peter Cordes
Nov 15 '18 at 14:32
1
@reijin: Then there's no reason to save/restorerbpinside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.
– Peter Cordes
Nov 15 '18 at 14:39
|
show 4 more comments
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
1
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
1
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
1
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it withleaveorpop rbp.
– Peter Cordes
Nov 15 '18 at 14:32
1
@reijin: Then there's no reason to save/restorerbpinside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.
– Peter Cordes
Nov 15 '18 at 14:39
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
Dude, you are a lifesaver! I learn so much from your responses. Thanks again! edit: will accept once I tested it tomorrow. Not sure if my way of calling the function is the best way, but I'm trying to emulate what Golang does when calling a function.
– reijin
Nov 14 '18 at 0:10
1
1
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
@reijin: updated with an example of clobbers + register inputs that should allow inputs to be in call-clobbered registers, at the compiler's option.
– Peter Cordes
Nov 15 '18 at 12:50
1
1
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
@reijin: I don't know what registers Go treats as call-clobbered, but I just updated my answer with the complete set of call-clobbered x86-64 System V registers.
– Peter Cordes
Nov 15 '18 at 14:29
1
1
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it with
leave or pop rbp.– Peter Cordes
Nov 15 '18 at 14:32
@reijin: Are you sure Go doesn't restore the caller's RBP before returning? Part of making a traditional stack frame is saving the caller's RBP so you can restore it with
leave or pop rbp.– Peter Cordes
Nov 15 '18 at 14:32
1
1
@reijin: Then there's no reason to save/restore
rbp inside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.– Peter Cordes
Nov 15 '18 at 14:39
@reijin: Then there's no reason to save/restore
rbp inside your inline asm. It's just like RBX or R15 as far as the compiler is concerned.– Peter Cordes
Nov 15 '18 at 14:39
|
show 4 more comments
The original poster added this solution as an edit to their question:
If someone ever finds this, the accepted answer does not help you when you try to call golang code with inline asm! The accepted answer only helps with my initial problem, which helped me to fix the golangcall. Use something like this:**
void* __cdecl go_call(void* func, __int64 p1, __int64 p2, __int64 p3, __int64 p4){
void* ret;
asm volatile(" sub rsp, 0x28; tn
mov [rsp], %[p1]; tn
mov [rsp+0x8], %[p2]; tn
mov [rsp+0x10], %[p3]; tn
mov [rsp+0x18], %[p4]; tn
call %[func_addr]; tn
add rsp, 0x28; "
:
: [p1] "ri"(p1), [p2] "ri"(p2),
[p3] "ri"(p3), [p4] "ri"(p4), [func_addr] "ri"(func)
: );
return ret;
}
1
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.sub rsp, 0x80/ 4x push / call /add rsp, 0x80 + 4*8should do the trick, along with clobbers as shown in my answer. There's no need to usemovto put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)
– Peter Cordes
Nov 21 '18 at 23:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes: Okok, I get it. I prefer themovway, as it doesn't force me to do the additionalsubcall. But I guess it's clearer to usepush. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!
– reijin
Nov 22 '18 at 17:00
@reijin: If you ignore the red-zone, it'smovthat needs asub rsp, offset. Withpushyou can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need onesuband oneadd. The only advantage tomovis that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).
– Peter Cordes
Nov 22 '18 at 17:55
2
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
add a comment |
The original poster added this solution as an edit to their question:
If someone ever finds this, the accepted answer does not help you when you try to call golang code with inline asm! The accepted answer only helps with my initial problem, which helped me to fix the golangcall. Use something like this:**
void* __cdecl go_call(void* func, __int64 p1, __int64 p2, __int64 p3, __int64 p4){
void* ret;
asm volatile(" sub rsp, 0x28; tn
mov [rsp], %[p1]; tn
mov [rsp+0x8], %[p2]; tn
mov [rsp+0x10], %[p3]; tn
mov [rsp+0x18], %[p4]; tn
call %[func_addr]; tn
add rsp, 0x28; "
:
: [p1] "ri"(p1), [p2] "ri"(p2),
[p3] "ri"(p3), [p4] "ri"(p4), [func_addr] "ri"(func)
: );
return ret;
}
1
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.sub rsp, 0x80/ 4x push / call /add rsp, 0x80 + 4*8should do the trick, along with clobbers as shown in my answer. There's no need to usemovto put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)
– Peter Cordes
Nov 21 '18 at 23:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes: Okok, I get it. I prefer themovway, as it doesn't force me to do the additionalsubcall. But I guess it's clearer to usepush. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!
– reijin
Nov 22 '18 at 17:00
@reijin: If you ignore the red-zone, it'smovthat needs asub rsp, offset. Withpushyou can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need onesuband oneadd. The only advantage tomovis that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).
– Peter Cordes
Nov 22 '18 at 17:55
2
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
add a comment |
The original poster added this solution as an edit to their question:
If someone ever finds this, the accepted answer does not help you when you try to call golang code with inline asm! The accepted answer only helps with my initial problem, which helped me to fix the golangcall. Use something like this:**
void* __cdecl go_call(void* func, __int64 p1, __int64 p2, __int64 p3, __int64 p4){
void* ret;
asm volatile(" sub rsp, 0x28; tn
mov [rsp], %[p1]; tn
mov [rsp+0x8], %[p2]; tn
mov [rsp+0x10], %[p3]; tn
mov [rsp+0x18], %[p4]; tn
call %[func_addr]; tn
add rsp, 0x28; "
:
: [p1] "ri"(p1), [p2] "ri"(p2),
[p3] "ri"(p3), [p4] "ri"(p4), [func_addr] "ri"(func)
: );
return ret;
}
The original poster added this solution as an edit to their question:
If someone ever finds this, the accepted answer does not help you when you try to call golang code with inline asm! The accepted answer only helps with my initial problem, which helped me to fix the golangcall. Use something like this:**
void* __cdecl go_call(void* func, __int64 p1, __int64 p2, __int64 p3, __int64 p4){
void* ret;
asm volatile(" sub rsp, 0x28; tn
mov [rsp], %[p1]; tn
mov [rsp+0x8], %[p2]; tn
mov [rsp+0x10], %[p3]; tn
mov [rsp+0x18], %[p4]; tn
call %[func_addr]; tn
add rsp, 0x28; "
:
: [p1] "ri"(p1), [p2] "ri"(p2),
[p3] "ri"(p3), [p4] "ri"(p4), [func_addr] "ri"(func)
: );
return ret;
}
answered Nov 21 '18 at 22:26
community wiki
Michael Petch
1
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.sub rsp, 0x80/ 4x push / call /add rsp, 0x80 + 4*8should do the trick, along with clobbers as shown in my answer. There's no need to usemovto put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)
– Peter Cordes
Nov 21 '18 at 23:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes: Okok, I get it. I prefer themovway, as it doesn't force me to do the additionalsubcall. But I guess it's clearer to usepush. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!
– reijin
Nov 22 '18 at 17:00
@reijin: If you ignore the red-zone, it'smovthat needs asub rsp, offset. Withpushyou can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need onesuband oneadd. The only advantage tomovis that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).
– Peter Cordes
Nov 22 '18 at 17:55
2
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
add a comment |
1
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.sub rsp, 0x80/ 4x push / call /add rsp, 0x80 + 4*8should do the trick, along with clobbers as shown in my answer. There's no need to usemovto put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)
– Peter Cordes
Nov 21 '18 at 23:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes: Okok, I get it. I prefer themovway, as it doesn't force me to do the additionalsubcall. But I guess it's clearer to usepush. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!
– reijin
Nov 22 '18 at 17:00
@reijin: If you ignore the red-zone, it'smovthat needs asub rsp, offset. Withpushyou can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need onesuband oneadd. The only advantage tomovis that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).
– Peter Cordes
Nov 22 '18 at 17:55
2
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
1
1
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.
sub rsp, 0x80 / 4x push / call / add rsp, 0x80 + 4*8 should do the trick, along with clobbers as shown in my answer. There's no need to use mov to put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)– Peter Cordes
Nov 21 '18 at 23:01
This "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler about the call-clobbered registers, and you step on the red-zone because you don't sub enough stack space to go past it.
sub rsp, 0x80 / 4x push / call / add rsp, 0x80 + 4*8 should do the trick, along with clobbers as shown in my answer. There's no need to use mov to put args on the stack, push works perfectly fine. (@michael: I know this will ping you, but I'm posting for the benefit of future readers.)– Peter Cordes
Nov 21 '18 at 23:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes :Understood. For other readers - my intent of posting this was to capture the OP's late edit, and I was taking no technical responsibility for it ;-). It was marked a community Wiki as I felt it was probably at least reasonable to move his update to an answer whether it be a good or bad answer. What I forgot to do was remove his solution from the question.Oops.
– Michael Petch
Nov 22 '18 at 0:01
@PeterCordes: Okok, I get it. I prefer the
mov way, as it doesn't force me to do the additional sub call. But I guess it's clearer to use push. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!– reijin
Nov 22 '18 at 17:00
@PeterCordes: Okok, I get it. I prefer the
mov way, as it doesn't force me to do the additional sub call. But I guess it's clearer to use push. Regarding your point on redzone and clubbered registers: I'm aware of it, it's just that I didn't run into any errors (yet), so this works fine for now. Your answer is still the better and more fleshed-out one!– reijin
Nov 22 '18 at 17:00
@reijin: If you ignore the red-zone, it's
mov that needs a sub rsp, offset. With push you can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need one sub and one add. The only advantage to mov is that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).– Peter Cordes
Nov 22 '18 at 17:55
@reijin: If you ignore the red-zone, it's
mov that needs a sub rsp, offset. With push you can just start pushing to move RSP for free. But since you need to skip over the red-zone, both ways need one sub and one add. The only advantage to mov is that you can see explicitly which offset you're storing what, instead of having to push in reverse order of args. (right-most arg first, so it's at the highest address).– Peter Cordes
Nov 22 '18 at 17:55
2
2
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
@reijin: it's common to write inline asm that happens to work in one test, but is actually broken and will cause hard-to-debug problems down the road after changes to some unrelated code. Or just with a new compiler version and/or build options (especially link-time optimization enabling inlining across source files.)
– Peter Cordes
Nov 22 '18 at 17:58
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287194%2ftoo-many-memory-references-for-mov-when-calling-a-golang-function-with-c-by-us%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Post your answer as an answer, not an edit to the question. And BTW, you can use
pushif you simply change the number that yousubfrom RSP to account for it. Your "answer" has multiple bugs that I warned about in my answer: you don't tell the compiler that the call-clobbered registers are clobbered, and you step on the red-zone because you don'tsubenough stack space to go past it.– Peter Cordes
Nov 21 '18 at 21:58