-
-
Notifications
You must be signed in to change notification settings - Fork 267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vararg functions does not compile on x86_64 #702
Comments
I believe it is the same bug as #73 |
Yes, it is indeed the same error. Closing this as duplicate. |
I don't think it is. #73 is about the implicit |
core.sys.windows.windows: Add ERROR_ALREADY_EXISTS
|
On Win64, Kai's test case works when importing core.stdc.stdarg instead of core.vararg. The joy vanishes as soon as you put
yields something like:
Also note that the float parameter 3.0f isn't passed correctly to log with D calling convention. /edit: Well I hope |
Regarding diff --git a/src/core/stdc/stdarg.d b/src/core/stdc/stdarg.d
index e53ec4f..c8c02ba 100644
--- a/src/core/stdc/stdarg.d
+++ b/src/core/stdc/stdarg.d
@@ -435,7 +435,7 @@ else version ( LDC_X86_64 )
// magic built-in type that on x86_64 decays to a reference like the actual
// definition in C (one-element array), or by giving va_copy a different
// signature.
- alias va_list = __va_list*;
+ alias va_list = char*;//__va_list*;
pragma(LDC_va_start)
void va_start(T)(out va_list ap, ref T);
@@ -449,12 +449,12 @@ else version ( LDC_X86_64 )
void va_arg(T)(va_list apx, ref T parmn)
{
- va_arg_x86_64(apx, parmn);
+ va_arg_x86_64(cast(__va_list*) apx, parmn);
}
void va_arg()(va_list apx, TypeInfo ti, void* parmn)
{
- va_arg_x86_64(apx, ti, parmn);
+ va_arg_x86_64(cast(__va_list*) apx, ti, parmn);
}
pragma(LDC_va_end) I also tried to use |
So from the ABI side, we seem to be doing the right thing when calling extern(C) vararg functions, otherwise printf() wouldn't work. This means that core.stdc.stdarg needs to be modified so that the extern(C) vararg implementations in druntime (such as these crucial array functions) work properly. In order not to have to differentiate between C and D linkage (I guess that's why core.vararg and core.stdc.stdarg currently differ), we would also need to modify the ABI when calling extern(D) vararg functions (analog to the extern(C) case, with the exception of the additional TypeInfo[] argument before the optional ones). Is that correct? If so, I'll try to work on it. The current state on Win64 is that core.vararg doesn't work for extern(C) as well as extern(D). core.stdc.stdarg seems to almost work with extern(D) (i.e., the current ABI for D linkage) but doesn't with extern(C). |
@kinke: There is one more issue, see #172. You are right, on all 64 bit x86 platforms the extern(C) and extern(D) ABIs should be identical, expect of course for the implicitly added TypeInfo array for D varargs. LDC was first regarding proper Linux x86_64 support, and back then they just chose a solution for varargs that doesn't match up with what DMD does. Ever since the initial D2 port, everything varargs is a convoluted mess. The __va_list type definition you linked to above is specific to the System V AMD64 ABI (i.e. most Unix-like systems). It should definitely not be used on Win64. It would be great if you could finally resolve the situation. Feel free to ping me with questions, as I looked into this a while ago (but don't have the time to fix this myself right now. |
Thanks David for clarifying. I'll only focus on Win64 here. So what I gathered is that varargs are passed just like normal args, i.e., the first 4 ones are passed in registers (integer or XMM for float/double), the rest on the stack. The caller always allocates 4x8 extra bytes just before pushing the return address. This space has been specifically designed for vararg functions to dump the 4 register args into, right below the remaining args. For vararg callees, MS chose to pass a float/double register arg in both corresponding integer and XMM registers. This means that the vararg callee would first need to dump rcx, rdx, r8 and r9 into rbp+16, rbp+24, rbp+32 and rbp+40: extern(C) long simpleTest(long a, long b, long c, long d, long e, long f)
{
asm
{
mov [RBP+16], RCX; // a
mov [RBP+24], RDX;
mov [RBP+32], R8;
mov [RBP+40], R9; // d
mov RAX, [RBP+48]; // return e
}
}
void main()
{
assert(simpleTest(1, 2, 3, 4, 5, 6) == 5);
} A va_list (MS defines it as char*) starting with the 2nd argument would therefore be initialized to rbp+24 by va_start(). The problem is that I cannot take the address of the last regular argument to deduce the offset of the first optional argument -- if there are no more than 4 regular arguments before the optional ones, the last one is passed in a register and not on the stack. void va_start(T)(out va_list ap, ref T parmn) { ap = &parmn.va; } @redstar @klickverbot @Trass3r What's that And btw, the argument order is reversed when using the extern(D) ABI. I guess that should be fixed as well then. |
@kinke: That's from DMD's implementation of System V AMD64-style varargs. Walter couldn't be bothered to (or couldn't mange to) properly implement them in his backend, so the user is expected to pass |
Also, as far as the vararg implementation goes, can't you use the LLVM vararg intrinsics for figuring out the address? As always, the TypeInfo overload might be a bit tricky, though. |
Use of the LLVM vararg intrinsics should be possible. I did the same for PPC. The situation is the same: the first parameters are passed in registers with backing store allocated on the stack. |
I've already tried the LLVM intrinsic, which returned garbage iirc. LLVM has a |
Okay the LLVM intrinsics work fine -- I previously tested the LDC-specific import core.stdc.stdio;
pragma(LDC_intrinsic, "llvm.va_start")
void llvm_va_start(void* pArglist);
pragma(LDC_intrinsic, "llvm.va_end")
void llvm_va_end(void* pArglist);
extern(C) void log(const(char)* str, ...)
{
char* args;
llvm_va_start(&args);
vprintf(str, args);
llvm_va_end(&args);
}
void main()
{
log ("log: %d %f %f %d %d %s\n", 1, 2.0, 3.0f, 4, 5, "lala".ptr);
printf("printf: %d %f %f %d %d %s\n", 1, 2.0, 3.0f, 4, 5, "lala".ptr);
} Unfortunately, one must pass the address of the va_list object to va_start() and va_end() because the parameters cannot be declared /edit: Ah wait, if I understand this correctly, this seems to be done normally anyway in |
Fixed by #768 and ldc-developers/druntime#14. |
The following module
compiles with dmd but gives an error with ldc:
Only on x86_64. The implementation of
core.vararg
is wrong.The text was updated successfully, but these errors were encountered: