Commit 73f5010d authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

runtime: allow to call stdcall from Go on windows

I've started with just one function with 8 arguments,
but stdcall is called from nosplit functions
and 8 args overflow nosplit area.

LGTM=aram, alex.brainman
R=golang-codereviews, aram, alex.brainman, dave
CC=golang-codereviews, iant, khr, rsc
https://golang.org/cl/135090043
parent 9f0b749b
......@@ -29,7 +29,7 @@ void*
runtime·SysAlloc(uintptr n, uint64 *stat)
{
runtime·xadd64(stat, n);
return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_READWRITE);
return runtime·stdcall4(runtime·VirtualAlloc, 0, n, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
}
void
......@@ -38,7 +38,7 @@ runtime·SysUnused(void *v, uintptr n)
void *r;
uintptr small;
r = runtime·stdcall(runtime·VirtualFree, 3, v, n, (uintptr)MEM_DECOMMIT);
r = runtime·stdcall3(runtime·VirtualFree, (uintptr)v, n, MEM_DECOMMIT);
if(r != nil)
return;
......@@ -53,7 +53,7 @@ runtime·SysUnused(void *v, uintptr n)
// in the worst case, but that's fast enough.
while(n > 0) {
small = n;
while(small >= 4096 && runtime·stdcall(runtime·VirtualFree, 3, v, small, (uintptr)MEM_DECOMMIT) == nil)
while(small >= 4096 && runtime·stdcall3(runtime·VirtualFree, (uintptr)v, small, MEM_DECOMMIT) == nil)
small = (small / 2) & ~(4096-1);
if(small < 4096)
runtime·throw("runtime: failed to decommit pages");
......@@ -67,7 +67,7 @@ runtime·SysUsed(void *v, uintptr n)
{
void *r;
r = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
r = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_COMMIT, PAGE_READWRITE);
if(r != v)
runtime·throw("runtime: failed to commit pages");
}
......@@ -78,7 +78,7 @@ runtime·SysFree(void *v, uintptr n, uint64 *stat)
uintptr r;
runtime·xadd64(stat, -(uint64)n);
r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, (uintptr)0, (uintptr)MEM_RELEASE);
r = (uintptr)runtime·stdcall3(runtime·VirtualFree, (uintptr)v, 0, MEM_RELEASE);
if(r == 0)
runtime·throw("runtime: failed to release pages");
}
......@@ -96,12 +96,12 @@ runtime·SysReserve(void *v, uintptr n, bool *reserved)
*reserved = true;
// v is just a hint.
// First try at v.
v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
v = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_RESERVE, PAGE_READWRITE);
if(v != nil)
return v;
// Next let the kernel choose the address.
return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
return runtime·stdcall4(runtime·VirtualAlloc, 0, n, MEM_RESERVE, PAGE_READWRITE);
}
void
......@@ -112,7 +112,7 @@ runtime·SysMap(void *v, uintptr n, bool reserved, uint64 *stat)
USED(reserved);
runtime·xadd64(stat, n);
p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
p = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_COMMIT, PAGE_READWRITE);
if(p != v)
runtime·throw("runtime: cannot map pages in arena address space");
}
......@@ -47,7 +47,7 @@ static uintptr iocphandle = INVALID_HANDLE_VALUE; // completion port io handle
void
runtime·netpollinit(void)
{
iocphandle = (uintptr)runtime·stdcall(runtime·CreateIoCompletionPort, 4, INVALID_HANDLE_VALUE, (uintptr)0, (uintptr)0, (uintptr)DWORD_MAX);
iocphandle = (uintptr)runtime·stdcall4(runtime·CreateIoCompletionPort, INVALID_HANDLE_VALUE, 0, 0, DWORD_MAX);
if(iocphandle == 0) {
runtime·printf("netpoll: failed to create iocp handle (errno=%d)\n", runtime·getlasterror());
runtime·throw("netpoll: failed to create iocp handle");
......@@ -59,7 +59,7 @@ int32
runtime·netpollopen(uintptr fd, PollDesc *pd)
{
USED(pd);
if(runtime·stdcall(runtime·CreateIoCompletionPort, 4, fd, iocphandle, (uintptr)0, (uintptr)0) == 0)
if(runtime·stdcall4(runtime·CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0)
return -runtime·getlasterror();
return 0;
}
......@@ -103,7 +103,7 @@ retry:
n = 8;
if(block)
g->m->blocked = true;
if(runtime·stdcall(runtime·GetQueuedCompletionStatusEx, 6, iocphandle, entries, (uintptr)n, &n, (uintptr)wait, (uintptr)0) == 0) {
if(runtime·stdcall6(runtime·GetQueuedCompletionStatusEx, iocphandle, (uintptr)entries, n, (uintptr)&n, wait, 0) == 0) {
g->m->blocked = false;
errno = runtime·getlasterror();
if(!block && errno == WAIT_TIMEOUT)
......@@ -116,7 +116,7 @@ retry:
op = entries[i].op;
errno = 0;
qty = 0;
if(runtime·stdcall(runtime·WSAGetOverlappedResult, 5, runtime·netpollfd(op->pd), op, &qty, (uintptr)0, (uintptr)&flags) == 0)
if(runtime·stdcall5(runtime·WSAGetOverlappedResult, runtime·netpollfd(op->pd), (uintptr)op, (uintptr)&qty, 0, (uintptr)&flags) == 0)
errno = runtime·getlasterror();
handlecompletion(&gp, op, errno, qty);
}
......@@ -126,7 +126,7 @@ retry:
qty = 0;
if(block)
g->m->blocked = true;
if(runtime·stdcall(runtime·GetQueuedCompletionStatus, 5, iocphandle, &qty, &key, &op, (uintptr)wait) == 0) {
if(runtime·stdcall5(runtime·GetQueuedCompletionStatus, iocphandle, (uintptr)&qty, (uintptr)&key, (uintptr)&op, wait) == 0) {
g->m->blocked = false;
errno = runtime·getlasterror();
if(!block && errno == WAIT_TIMEOUT)
......
......@@ -81,7 +81,7 @@ getproccount(void)
{
SystemInfo info;
runtime·stdcall(runtime·GetSystemInfo, 1, &info);
runtime·stdcall1(runtime·GetSystemInfo, (uintptr)&info);
return info.dwNumberOfProcessors;
}
......@@ -92,20 +92,20 @@ runtime·osinit(void)
runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;
runtime·stdcall(runtime·AddVectoredExceptionHandler, 2, (uintptr)1, (uintptr)runtime·sigtramp);
runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1);
runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·sigtramp);
runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1);
runtime·stdcall1(runtime·timeBeginPeriod, 1);
runtime·ncpu = getproccount();
// Windows dynamic priority boosting assumes that a process has different types
// of dedicated threads -- GUI, IO, computational, etc. Go processes use
// equivalent threads that all do a mix of GUI, IO, computations, etc.
// In such context dynamic priority boosting does nothing but harm, so we turn it off.
runtime·stdcall(runtime·SetProcessPriorityBoost, 2, (uintptr)-1, (uintptr)1);
runtime·stdcall2(runtime·SetProcessPriorityBoost, -1, 1);
kernel32 = runtime·stdcall(runtime·LoadLibraryA, 1, "kernel32.dll");
kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
if(kernel32 != nil) {
runtime·GetQueuedCompletionStatusEx = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "GetQueuedCompletionStatusEx");
runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx");
}
}
......@@ -115,15 +115,15 @@ runtime·get_random_data(byte **rnd, int32 *rnd_len)
uintptr handle;
*rnd = nil;
*rnd_len = 0;
if(runtime·stdcall(runtime·CryptAcquireContextW, 5, &handle, nil, nil,
(uintptr)1 /* PROV_RSA_FULL */,
(uintptr)0xf0000000U /* CRYPT_VERIFYCONTEXT */) != 0) {
if(runtime·stdcall5(runtime·CryptAcquireContextW, (uintptr)&handle, (uintptr)nil, (uintptr)nil,
1 /* PROV_RSA_FULL */,
0xf0000000U /* CRYPT_VERIFYCONTEXT */) != 0) {
static byte random_data[HashRandomBytes];
if(runtime·stdcall(runtime·CryptGenRandom, 3, handle, (uintptr)HashRandomBytes, random_data)) {
if(runtime·stdcall3(runtime·CryptGenRandom, handle, HashRandomBytes, (uintptr)&random_data[0])) {
*rnd = random_data;
*rnd_len = HashRandomBytes;
}
runtime·stdcall(runtime·CryptReleaseContext, 2, handle, (uintptr)0);
runtime·stdcall2(runtime·CryptReleaseContext, handle, 0);
}
}
......@@ -137,7 +137,7 @@ runtime·goenvs(void)
int32 i, n;
uint16 *p;
env = runtime·stdcall(runtime·GetEnvironmentStringsW, 0);
env = runtime·stdcall0(runtime·GetEnvironmentStringsW);
n = 0;
for(p=env; *p; n++)
......@@ -154,13 +154,13 @@ runtime·goenvs(void)
syscall·envs.len = n;
syscall·envs.cap = n;
runtime·stdcall(runtime·FreeEnvironmentStringsW, 1, env);
runtime·stdcall1(runtime·FreeEnvironmentStringsW, (uintptr)env);
}
void
runtime·exit(int32 code)
{
runtime·stdcall(runtime·ExitProcess, 1, (uintptr)code);
runtime·stdcall1(runtime·ExitProcess, code);
}
int32
......@@ -172,17 +172,17 @@ runtime·write(uintptr fd, void *buf, int32 n)
written = 0;
switch(fd) {
case 1:
handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-11);
handle = runtime·stdcall1(runtime·GetStdHandle, -11);
break;
case 2:
handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-12);
handle = runtime·stdcall1(runtime·GetStdHandle, -12);
break;
default:
// assume fd is real windows handle.
handle = (void*)fd;
break;
}
runtime·stdcall(runtime·WriteFile, 5, handle, buf, (uintptr)n, &written, (uintptr)0);
runtime·stdcall5(runtime·WriteFile, (uintptr)handle, (uintptr)buf, n, (uintptr)&written, 0);
return written;
}
......@@ -200,7 +200,7 @@ runtime·semasleep(int64 ns)
if(ns == 0)
ns = 1;
}
if(runtime·stdcall(runtime·WaitForSingleObject, 2, g->m->waitsema, (uintptr)ns) != 0)
if(runtime·stdcall2(runtime·WaitForSingleObject, (uintptr)g->m->waitsema, ns) != 0)
return -1; // timeout
return 0;
}
......@@ -208,13 +208,13 @@ runtime·semasleep(int64 ns)
void
runtime·semawakeup(M *mp)
{
runtime·stdcall(runtime·SetEvent, 1, mp->waitsema);
runtime·stdcall1(runtime·SetEvent, mp->waitsema);
}
uintptr
runtime·semacreate(void)
{
return (uintptr)runtime·stdcall(runtime·CreateEvent, 4, (uintptr)0, (uintptr)0, (uintptr)0, (uintptr)0);
return (uintptr)runtime·stdcall4(runtime·CreateEvent, 0, 0, 0, 0);
}
#define STACK_SIZE_PARAM_IS_A_RESERVATION ((uintptr)0x00010000)
......@@ -226,9 +226,9 @@ runtime·newosproc(M *mp, void *stk)
USED(stk);
thandle = runtime·stdcall(runtime·CreateThread, 6,
nil, (uintptr)0x20000, runtime·tstart_stdcall, mp,
STACK_SIZE_PARAM_IS_A_RESERVATION, nil);
thandle = runtime·stdcall6(runtime·CreateThread,
(uintptr)nil, 0x20000, (uintptr)runtime·tstart_stdcall, (uintptr)mp,
STACK_SIZE_PARAM_IS_A_RESERVATION, (uintptr)nil);
if(thandle == nil) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror());
runtime·throw("runtime.newosproc");
......@@ -251,9 +251,7 @@ runtime·minit(void)
void *thandle;
// -1 = current process, -2 = current thread
runtime·stdcall(runtime·DuplicateHandle, 7,
(uintptr)-1, (uintptr)-2, (uintptr)-1, &thandle,
(uintptr)0, (uintptr)0, (uintptr)DUPLICATE_SAME_ACCESS);
runtime·stdcall7(runtime·DuplicateHandle, -1, -2, -1, (uintptr)&thandle, 0, 0, DUPLICATE_SAME_ACCESS);
runtime·atomicstorep(&g->m->thread, thandle);
}
......@@ -318,12 +316,10 @@ time·now(int64 sec, int32 usec)
// Calling stdcall on os stack.
#pragma textflag NOSPLIT
void *
runtime·stdcall(void *fn, int32 count, ...)
static void*
stdcall(void *fn)
{
g->m->libcall.fn = fn;
g->m->libcall.n = count;
g->m->libcall.args = (uintptr*)&count + 1;
if(g->m->profilehz != 0) {
// leave pc/sp for cpu profiler
g->m->libcallg = g;
......@@ -337,6 +333,85 @@ runtime·stdcall(void *fn, int32 count, ...)
return (void*)g->m->libcall.r1;
}
#pragma textflag NOSPLIT
void*
runtime·stdcall0(void *fn)
{
g->m->libcall.n = 0;
g->m->libcall.args = &fn; // it's unused but must be non-nil, otherwise crashes
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall1(void *fn, uintptr a0)
{
USED(a0);
g->m->libcall.n = 1;
g->m->libcall.args = &a0;
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall2(void *fn, uintptr a0, uintptr a1)
{
USED(a0, a1);
g->m->libcall.n = 2;
g->m->libcall.args = &a0;
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall3(void *fn, uintptr a0, uintptr a1, uintptr a2)
{
USED(a0, a1, a2);
g->m->libcall.n = 3;
g->m->libcall.args = &a0;
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall4(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3)
{
USED(a0, a1, a2, a3);
g->m->libcall.n = 4;
g->m->libcall.args = &a0;
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall5(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4)
{
USED(a0, a1, a2, a3, a4);
g->m->libcall.n = 5;
g->m->libcall.args = &a0;
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall6(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5)
{
USED(a0, a1, a2, a3, a4, a5);
g->m->libcall.n = 6;
g->m->libcall.args = &a0;
return stdcall(fn);
}
#pragma textflag NOSPLIT
void*
runtime·stdcall7(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6)
{
USED(a0, a1, a2, a3, a4, a5, a6);
g->m->libcall.n = 7;
g->m->libcall.args = &a0;
return stdcall(fn);
}
extern void runtime·usleep1(uint32);
#pragma textflag NOSPLIT
......@@ -451,7 +526,7 @@ profilem(M *mp)
// align Context to 16 bytes
r = (Context*)((uintptr)(&rbuf[15]) & ~15);
r->ContextFlags = CONTEXT_CONTROL;
runtime·stdcall(runtime·GetThreadContext, 2, mp->thread, r);
runtime·stdcall2(runtime·GetThreadContext, (uintptr)mp->thread, (uintptr)r);
runtime·dosigprof(r, gp, mp);
}
......@@ -461,11 +536,10 @@ runtime·profileloop1(void)
M *mp, *allm;
void *thread;
runtime·stdcall(runtime·SetThreadPriority, 2,
(uintptr)-2, (uintptr)THREAD_PRIORITY_HIGHEST);
runtime·stdcall2(runtime·SetThreadPriority, -2, THREAD_PRIORITY_HIGHEST);
for(;;) {
runtime·stdcall(runtime·WaitForSingleObject, 2, profiletimer, (uintptr)-1);
runtime·stdcall2(runtime·WaitForSingleObject, (uintptr)profiletimer, -1);
allm = runtime·atomicloadp(&runtime·allm);
for(mp = allm; mp != nil; mp = mp->alllink) {
thread = runtime·atomicloadp(&mp->thread);
......@@ -474,10 +548,10 @@ runtime·profileloop1(void)
// idle timer thread, idle heap scavenger, etc.
if(thread == nil || mp->profilehz == 0 || mp->blocked)
continue;
runtime·stdcall(runtime·SuspendThread, 1, thread);
runtime·stdcall1(runtime·SuspendThread, (uintptr)thread);
if(mp->profilehz != 0 && !mp->blocked)
profilem(mp);
runtime·stdcall(runtime·ResumeThread, 1, thread);
runtime·stdcall1(runtime·ResumeThread, (uintptr)thread);
}
}
}
......@@ -492,11 +566,11 @@ runtime·resetcpuprofiler(int32 hz)
runtime·lock(&lock);
if(profiletimer == nil) {
timer = runtime·stdcall(runtime·CreateWaitableTimer, 3, nil, nil, nil);
timer = runtime·stdcall3(runtime·CreateWaitableTimer, (uintptr)nil, (uintptr)nil, (uintptr)nil);
runtime·atomicstorep(&profiletimer, timer);
thread = runtime·stdcall(runtime·CreateThread, 6,
nil, nil, runtime·profileloop, nil, nil, nil);
runtime·stdcall(runtime·CloseHandle, 1, thread);
thread = runtime·stdcall6(runtime·CreateThread,
(uintptr)nil, (uintptr)nil, (uintptr)runtime·profileloop, (uintptr)nil, (uintptr)nil, (uintptr)nil);
runtime·stdcall1(runtime·CloseHandle, (uintptr)thread);
}
runtime·unlock(&lock);
......@@ -508,8 +582,8 @@ runtime·resetcpuprofiler(int32 hz)
ms = 1;
due = ms * -10000;
}
runtime·stdcall(runtime·SetWaitableTimer, 6,
profiletimer, &due, (uintptr)ms, nil, nil, nil);
runtime·stdcall6(runtime·SetWaitableTimer,
(uintptr)profiletimer, (uintptr)&due, ms, (uintptr)nil, (uintptr)nil, (uintptr)nil);
runtime·atomicstore((uint32*)&g->m->profilehz, hz);
}
......
......@@ -6,6 +6,17 @@ package runtime
import "unsafe"
type stdFunction *byte
func stdcall0(fn stdFunction) uintptr
func stdcall1(fn stdFunction, a0 uintptr) uintptr
func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr
func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr
func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr
func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr
func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr
func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr
func asmstdcall(fn unsafe.Pointer)
func getlasterror() uint32
func setlasterror(err uint32)
......
......@@ -8,11 +8,15 @@ extern void *runtime·GetQueuedCompletionStatusEx;
// Call a Windows function with stdcall conventions,
// and switch to os stack during the call.
#pragma varargck countpos runtime·stdcall 2
#pragma varargck type runtime·stdcall void*
#pragma varargck type runtime·stdcall uintptr
void runtime·asmstdcall(void *c);
void *runtime·stdcall(void *fn, int32 count, ...);
void *runtime·stdcall0(void *fn);
void *runtime·stdcall1(void *fn, uintptr a0);
void *runtime·stdcall2(void *fn, uintptr a0, uintptr a1);
void *runtime·stdcall3(void *fn, uintptr a0, uintptr a1, uintptr a2);
void *runtime·stdcall4(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3);
void *runtime·stdcall5(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4);
void *runtime·stdcall6(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5);
void *runtime·stdcall7(void *fn, uintptr a0, uintptr a1, uintptr a2, uintptr a3, uintptr a4, uintptr a5, uintptr a6);
uint32 runtime·getlasterror(void);
void runtime·setlasterror(uint32 err);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment