Having fun trying to figure out what's in the black box of _vcomp_fork(), the helper function that spawns threads in Visual C's OpenMP support library.
The C source code
#include <stdio.h> #include <omp.h> int main(int argc, char **argv) { double d; double e; int i; printf("&d is %p, &e is %p\n", &d, &e); #pragma omp parallel for for (i=0; i<3; i++) { printf("&d is %p\n", &d); } #pragma omp parallel for for (i=0; i<3; i++) { printf("&d is %p, &e is %p\n", &d, &e); } return 0; }
when compiled with cl /MD /openmp /FAs odd.c
generates an .exe and a mixed assembly / source .asm listing. The compiler puts the loop into a helper function, and invokes the helper using _vcomp_fork(), which is called with a flag saying whether to parallelize, a pointer to the helper function, a count, and then a pointer to each variable referenced by the loop from the outer scope. The code in the helper function knows to get the addresses of the variables from the stack.
When run with native vcomp.dll, this prints out the right addresses inside the loop. When run with my slightly less stubby builtin vcomp patch attached to http://bugs.winehq.org/show_bug.cgi?id=26688 (based on Andre's earlier patch), it prints out garbage.
It seems like Andre's varargs code should be right, but something's not connecting. I even tried beer, and it still didn't work.
I guess I should fire up a debugger and see what's going on, but I don't want to accidentally see too much about what native's doing.
Suggestions, anyone? - Dan
Am 09.08.2012 07:49, schrieb Dan Kegel:
It seems like Andre's varargs code should be right, but something's not connecting. I even tried beer, and it still didn't work.
:D maybe you should try to write it in assembler and later try to move things to C...
Yeah, it might be as simple as _vcomp_vfork proc add sp,4 ; skip parallel flag and arg count ret ; jump to helper function, leaving its args on stack
or something like that.
Yes indeed, this works:
extern void WINAPIV VCOMP__vcomp_fork(DWORD parallel, int ncount, void (__cdecl *helper)(__ms_va_list), ...); __ASM_GLOBAL_FUNC( VCOMP__vcomp_fork, "pop %eax\n\t" /* save return address */ "add $8,%esp\n\t" /* skip parallel and ncount */ "pop %ebx\n\t" /* save helper */ "push %eax\n\t" /* set up return address */ "jmp %ebx" /* enter handler */ )
On Fri, Aug 10, 2012 at 7:37 PM, Dan Kegel dank@kegel.com wrote:
Yes indeed, this works:
extern void WINAPIV VCOMP__vcomp_fork(DWORD parallel, int ncount, void (__cdecl *helper)(__ms_va_list), ...); __ASM_GLOBAL_FUNC( VCOMP__vcomp_fork, "pop %eax\n\t" /* save return address */ "add $8,%esp\n\t" /* skip parallel and ncount */ "pop %ebx\n\t" /* save helper */ "push %eax\n\t" /* set up return address */ "jmp %ebx" /* enter handler */ )
(Or maybe %edx instead of %ebx; http://en.wikipedia.org/wiki/X86_calling_conventions suggests %ebx shouldn't be modified by the callee.)
Using varargs like Andre tried doesn't work because pushing a va_list isn't the same as pushing a list of arguments. So I think we need assembly. The assembly I gave earlier is wrong because it cleans up the stack, which it's not allowed to do. I've attached an updated patch to http://bugs.winehq.org/show_bug.cgi?id=26688 with a _vcomp_fork that works for simple loops with and without bound variables.
I guess the next step is porting the serial stub for _vcomp_fork to 64 bits.