]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: do not generate code during runtime in windows NewCallback
authorAlex Brainman <alex.brainman@gmail.com>
Mon, 24 Jun 2013 07:17:45 +0000 (17:17 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Mon, 24 Jun 2013 07:17:45 +0000 (17:17 +1000)
Update #5494

R=golang-dev, minux.ma, rsc, iant
CC=golang-dev
https://golang.org/cl/10368043

src/cmd/dist/a.h
src/cmd/dist/build.c
src/cmd/dist/buildruntime.c
src/pkg/runtime/callback_windows.c [moved from src/pkg/runtime/callback_windows_386.c with 54% similarity]
src/pkg/runtime/callback_windows_amd64.c [deleted file]
src/pkg/runtime/runtime.h
src/pkg/runtime/sys_windows_386.s
src/pkg/runtime/sys_windows_amd64.s

index 73c126476e108dd7259345af5a610d0ed8e3e921..d8a13f92a0fe48bf9f3db95aae14c2913313af42 100644 (file)
@@ -94,6 +94,7 @@ void  mkenam(char*, char*);
 
 // buildruntime.c
 void   mkzasm(char*, char*);
+void   mkzsys(char*, char*);
 void   mkzgoarch(char*, char*);
 void   mkzgoos(char*, char*);
 void   mkzruntimedefs(char*, char*);
index cdab81deac56e1ef1536f53e7859a7fb228b6332..ba32d3e69b3d53e859412e463740ed900f3b923c 100644 (file)
@@ -528,6 +528,7 @@ static struct {
        }},
        {"pkg/runtime", {
                "zasm_$GOOS_$GOARCH.h",
+               "zsys_$GOOS_$GOARCH.s",
                "zgoarch_$GOARCH.go",
                "zgoos_$GOOS.go",
                "zruntime_defs_$GOOS_$GOARCH.go",
@@ -552,6 +553,7 @@ static struct {
        {"opnames.h", gcopnames},
        {"enam.c", mkenam},
        {"zasm_", mkzasm},
+       {"zsys_", mkzsys},
        {"zgoarch_", mkzgoarch},
        {"zgoos_", mkzgoos},
        {"zruntime_defs_", mkzruntimedefs},
index 8f3fc541633c847ec8b4e287be92a13a0775f792..2d221eb39c41a591a4416b307634968d337ca245 100644 (file)
@@ -178,6 +178,8 @@ static struct {
        },
 };
 
+#define MAXWINCB 2000 /* maximum number of windows callbacks allowed */
+
 // mkzasm writes zasm_$GOOS_$GOARCH.h,
 // which contains struct offsets for use by
 // assembly files.  It also writes a copy to the work space
@@ -249,6 +251,8 @@ ok:
                                aggr = "gobuf";
                        else if(streq(fields.p[1], "WinCall"))
                                aggr = "wincall";
+                       else if(streq(fields.p[1], "WinCallbackContext"))
+                               aggr = "cbctxt";
                        else if(streq(fields.p[1], "SEH"))
                                aggr = "seh";
                }
@@ -262,6 +266,11 @@ ok:
                        bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
                }
        }
+
+       // Some #defines that are used for .c files.
+       if(streq(goos, "windows")) {
+               bwritestr(&out, bprintf(&b, "#define cb_max %d\n", MAXWINCB));
+       }
        
        // Write both to file and to workdir/zasm_GOOS_GOARCH.h.
        writefile(&out, file, 0);
@@ -275,6 +284,41 @@ ok:
        vfree(&fields);
 }
 
+// mkzsys writes zsys_$GOOS_$GOARCH.h,
+// which contains arch or os specific asm code.
+// 
+void
+mkzsys(char *dir, char *file)
+{
+       int i;
+       Buf out;
+
+       USED(dir);
+       
+       binit(&out);
+       
+       bwritestr(&out, "// auto generated by go tool dist\n\n");
+       if(streq(goos, "windows")) {
+               bwritef(&out,
+                       "// runtime·callbackasm is called by external code to\n"
+                       "// execute Go implemented callback function. It is not\n"
+                       "// called from the start, instead runtime·compilecallback\n"
+                       "// always returns address into runtime·callbackasm offset\n"
+                       "// appropriately so different callbacks start with different\n"
+                       "// CALL instruction in runtime·callbackasm. This determines\n"
+                       "// which Go callback function is executed later on.\n"
+                       "TEXT runtime·callbackasm(SB),7,$0\n");
+               for(i=0; i<MAXWINCB; i++) {
+                       bwritef(&out, "\tCALL\truntime·callbackasm1(SB)\n");
+               }
+               bwritef(&out, "\tRET\n");
+       }
+
+       writefile(&out, file, 0);
+       
+       bfree(&out);
+}
+
 static char *runtimedefs[] = {
        "proc.c",
        "iface.c",
similarity index 54%
rename from src/pkg/runtime/callback_windows_386.c
rename to src/pkg/runtime/callback_windows.c
index 880588da636f048f3663effb6ffecf661534eae9..88ee53bb5eb5759320ea700ff8f207ac14c68ec0 100644 (file)
@@ -7,24 +7,19 @@
 #include "typekind.h"
 #include "defs_GOOS_GOARCH.h"
 #include "os_GOOS.h"
-
-// Will keep all callbacks in a linked list, so they don't get garbage collected.
-typedef        struct  Callback        Callback;
-struct Callback {
-       Callback*       link;
-       void*           gobody;
-       byte            asmbody;
-};
+#include "zasm_GOOS_GOARCH.h"
 
 typedef        struct  Callbacks       Callbacks;
 struct Callbacks {
        Lock;
-       Callback*       link;
-       int32           n;
+       WinCallbackContext*     ctxt[cb_max];
+       int32                   n;
 };
 
 static Callbacks       cbs;
 
+WinCallbackContext** runtime·cbctxts; // to simplify access to cbs.ctxt in sys_windows_*.s
+
 // Call back from windows dll into go.
 byte *
 runtime·compilecallback(Eface fn, bool cleanstack)
@@ -32,8 +27,7 @@ runtime·compilecallback(Eface fn, bool cleanstack)
        FuncType *ft;
        Type *t;
        int32 argsize, i, n;
-       byte *p;
-       Callback *c;
+       WinCallbackContext *c;
 
        if(fn.type == nil || fn.type->kind != KindFunc)
                runtime·panicstring("compilecallback: not a function");
@@ -50,59 +44,33 @@ runtime·compilecallback(Eface fn, bool cleanstack)
                argsize += sizeof(uintptr);
        }
 
-       // compute size of new fn.
-       // must match code laid out below.
-       n = 1+4;                // MOVL fn, AX
-       n += 1+4;               // MOVL argsize, DX
-       n += 1+4;               // MOVL callbackasm, CX
-       n += 2;                 // CALL CX
-       n += 1;                 // RET
-       if(cleanstack && argsize!=0)
-               n += 2;         // ... argsize
-
        runtime·lock(&cbs);
-       for(c = cbs.link; c != nil; c = c->link) {
-               if(c->gobody == fn.data) {
+       if(runtime·cbctxts == nil)
+               runtime·cbctxts = &(cbs.ctxt[0]);
+       n = cbs.n;
+       for(i=0; i<n; i++) {
+               if(cbs.ctxt[i]->gobody == fn.data) {
                        runtime·unlock(&cbs);
-                       return &c->asmbody;
+                       // runtime·callbackasm is just a series of CALL instructions
+                       // (each is 5 bytes long), and we want callback to arrive at
+                       // correspondent call instruction instead of start of
+                       // runtime·callbackasm.
+                       return (byte*)runtime·callbackasm + i * 5;
                }
        }
-       if(cbs.n >= 2000)
+       if(n >= cb_max)
                runtime·throw("too many callback functions");
-       c = runtime·mal(sizeof *c + n);
+       c = runtime·mal(sizeof *c);
        c->gobody = fn.data;
-       c->link = cbs.link;
-       cbs.link = c;
+       c->argsize = argsize;
+       if(cleanstack && argsize!=0)
+               c->restorestack = argsize;
+       else
+               c->restorestack = 0;
+       cbs.ctxt[n] = c;
        cbs.n++;
        runtime·unlock(&cbs);
 
-       p = &c->asmbody;
-
-       // MOVL fn, AX
-       *p++ = 0xb8;
-       *(uint32*)p = (uint32)(fn.data);
-       p += 4;
-
-       // MOVL argsize, DX
-       *p++ = 0xba;
-       *(uint32*)p = argsize;
-       p += 4;
-
-       // MOVL callbackasm, CX
-       *p++ = 0xb9;
-       *(uint32*)p = (uint32)runtime·callbackasm;
-       p += 4;
-
-       // CALL CX
-       *p++ = 0xff;
-       *p++ = 0xd1;
-
-       // RET argsize?
-       if(cleanstack && argsize!=0) {
-               *p++ = 0xc2;
-               *(uint16*)p = argsize;
-       } else
-               *p = 0xc3;
-
-       return &c->asmbody;
+       // as before
+       return (byte*)runtime·callbackasm + n * 5;
 }
diff --git a/src/pkg/runtime/callback_windows_amd64.c b/src/pkg/runtime/callback_windows_amd64.c
deleted file mode 100644 (file)
index 1a47792..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "runtime.h"
-#include "type.h"
-#include "typekind.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-
-// Will keep all callbacks in a linked list, so they don't get garbage collected.
-typedef        struct  Callback        Callback;
-struct Callback {
-       Callback*       link;
-       void*           gobody;
-       byte            asmbody;
-};
-
-typedef        struct  Callbacks       Callbacks;
-struct Callbacks {
-       Lock;
-       Callback*       link;
-       int32           n;
-};
-
-static Callbacks       cbs;
-
-// Call back from windows dll into go.
-byte *
-runtime·compilecallback(Eface fn, bool /*cleanstack*/)
-{
-       FuncType *ft;
-       Type *t;
-       int32 argsize, i, n;
-       byte *p;
-       Callback *c;
-
-       if(fn.type == nil || fn.type->kind != KindFunc)
-               runtime·panicstring("compilecallback: not a function");
-       ft = (FuncType*)fn.type;
-       if(ft->out.len != 1)
-               runtime·panicstring("compilecallback: function must have one output parameter");
-       if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
-               runtime·panicstring("compilecallback: output parameter size is wrong");
-       argsize = 0;
-       for(i=0; i<ft->in.len; i++) {
-               t = ((Type**)ft->in.array)[i];
-               if(t->size > sizeof(uintptr))
-                       runtime·panicstring("compilecallback: input parameter size is wrong");
-               argsize += sizeof(uintptr);
-       }
-
-       // compute size of new fn.
-       // must match code laid out below.
-       n  = 2+8+1; // MOVQ fn, AX           / PUSHQ AX
-       n += 2+8+1; // MOVQ argsize, AX      / PUSHQ AX
-       n += 2+8;   // MOVQ callbackasm, AX
-       n += 2;     // JMP  AX
-
-       runtime·lock(&cbs);
-       for(c = cbs.link; c != nil; c = c->link) {
-               if(c->gobody == fn.data) {
-                       runtime·unlock(&cbs);
-                       return &c->asmbody;
-               }
-       }
-       if(cbs.n >= 2000)
-               runtime·throw("too many callback functions");
-       c = runtime·mal(sizeof *c + n);
-       c->gobody = fn.data;
-       c->link = cbs.link;
-       cbs.link = c;
-       cbs.n++;
-       runtime·unlock(&cbs);
-
-       p = &c->asmbody;
-
-       // MOVQ fn, AX
-       *p++ = 0x48;
-       *p++ = 0xb8;
-       *(uint64*)p = (uint64)(fn.data);
-       p += 8;
-       // PUSH AX
-       *p++ = 0x50;
-
-       // MOVQ argsize, AX
-       *p++ = 0x48;
-       *p++ = 0xb8;
-       *(uint64*)p = argsize;
-       p += 8;
-       // PUSH AX
-       *p++ = 0x50;
-
-       // MOVQ callbackasm, AX
-       *p++ = 0x48;
-       *p++ = 0xb8;
-       *(uint64*)p = (uint64)runtime·callbackasm;
-       p += 8;
-
-       // JMP AX
-       *p++ = 0xFF;
-       *p = 0xE0;
-
-       return &c->asmbody;
-}
index f62ee81de48e421a39383aa052d7fed628960627..f5da54a2a81e56da422f80a429b5a676200da1d0 100644 (file)
@@ -78,6 +78,7 @@ typedef       struct  Complex64       Complex64;
 typedef        struct  Complex128      Complex128;
 typedef        struct  WinCall         WinCall;
 typedef        struct  SEH             SEH;
+typedef        struct  WinCallbackContext      WinCallbackContext;
 typedef        struct  Timers          Timers;
 typedef        struct  Timer           Timer;
 typedef        struct  GCStats         GCStats;
@@ -444,6 +445,13 @@ struct     SEH
        void*   prev;
        void*   handler;
 };
+// describes how to handle callback
+struct WinCallbackContext
+{
+       void*   gobody;         // Go function to call
+       uintptr argsize;        // callback arguments size (in bytes)
+       uintptr restorestack;   // adjust stack on return by (in bytes) (386 only)
+};
 
 #ifdef GOOS_windows
 enum {
index 2c63b33850bdfcfcbbfd5c8eb2cee869b0f8fc11..728fb99018955d9fca2f545bf1629f59a70f7986 100644 (file)
@@ -164,19 +164,16 @@ TEXT runtime·externalthreadhandler(SB),7,$0
        POPL    BP
        RET
 
-// Called from dynamic function created by ../thread.c compilecallback,
-// running on Windows stack (not Go stack).
-// BX, BP, SI, DI registers and DF flag are preserved
-// as required by windows callback convention.
-// AX = address of go func we need to call
-// DX = total size of arguments
-//
-TEXT runtime·callbackasm+0(SB),7,$0
-       // preserve whatever's at the memory location that
-       // the callback will use to store the return value
-       LEAL    8(SP), CX
-       PUSHL   0(CX)(DX*1)
-       ADDL    $4, DX                  // extend argsize by size of return value
+GLOBL runtime·cbctxts(SB), $4
+
+TEXT runtime·callbackasm1+0(SB),7,$0
+       MOVL    0(SP), AX       // will use to find our callback context
+
+       // remove return address from stack, we are not returning there
+       ADDL    $4, SP
+
+       // address to callback parameters into CX
+       LEAL    4(SP), CX
 
        // save registers as required for windows callback
        PUSHL   DI
@@ -189,19 +186,51 @@ TEXT runtime·callbackasm+0(SB),7,$0
        PUSHL   0(FS)
        MOVL    SP, 0(FS)
 
-       // callback parameters
-       PUSHL   DX
-       PUSHL   CX
-       PUSHL   AX
+       // determine index into runtime·cbctxts table
+       SUBL    $runtime·callbackasm(SB), AX
+       MOVL    $0, DX
+       MOVL    $5, BX  // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
+       DIVL    BX,
 
-       CLD
+       // find correspondent runtime·cbctxts table entry
+       MOVL    runtime·cbctxts(SB), BX
+       MOVL    -4(BX)(AX*4), BX
 
-       CALL    runtime·cgocallback_gofunc(SB)
+       // extract callback context
+       MOVL    cbctxt_gobody(BX), AX
+       MOVL    cbctxt_argsize(BX), DX
 
+       // preserve whatever's at the memory location that
+       // the callback will use to store the return value
+       PUSHL   0(CX)(DX*1)
+
+       // extend argsize by size of return value
+       ADDL    $4, DX
+
+       // remember how to restore stack on return
+       MOVL    cbctxt_restorestack(BX), BX
+       PUSHL   BX
+
+       // call target Go function
+       PUSHL   DX                      // argsize (including return value)
+       PUSHL   CX                      // callback parameters
+       PUSHL   AX                      // address of target Go function
+       CLD
+       CALL    runtime·cgocallback_gofunc(SB)
        POPL    AX
        POPL    CX
        POPL    DX
 
+       // how to restore stack on return
+       POPL    BX
+
+       // return value into AX (as per Windows spec)
+       // and restore previously preserved value
+       MOVL    -4(CX)(DX*1), AX
+       POPL    -4(CX)(DX*1)
+
+       MOVL    BX, CX                  // cannot use BX anymore
+
        // pop SEH frame
        POPL    0(FS)
        POPL    BX
@@ -212,10 +241,13 @@ TEXT runtime·callbackasm+0(SB),7,$0
        POPL    SI
        POPL    DI
 
+       // remove callback parameters before return (as per Windows spec)
+       POPL    DX
+       ADDL    CX, SP
+       PUSHL   DX
+
        CLD
 
-       MOVL    -4(CX)(DX*1), AX
-       POPL    -4(CX)(DX*1)
        RET
 
 // void tstart(M *newm);
index b9eaec68d71263d266dba1ad757d6477a21900f2..ca07f572b48152c5a45970ce7edfdd1783f15cb4 100644 (file)
@@ -196,32 +196,37 @@ TEXT runtime·externalthreadhandler(SB),7,$0
        POPQ    BP
        RET
 
-// Continuation of thunk function created for each callback by ../thread.c compilecallback,
-// runs on Windows stack (not Go stack).
-// Thunk code designed to have minimal size for it is copied many (up to thousands) times.
-//
-// thunk:
-//     MOVQ    $fn, AX
-//     PUSHQ   AX
-//     MOVQ    $argsize, AX
-//     PUSHQ   AX
-//     MOVQ    $runtime·callbackasm, AX
-//     JMP     AX
-TEXT runtime·callbackasm(SB),7,$0
+GLOBL runtime·cbctxts(SB), $8
+
+TEXT runtime·callbackasm1(SB),7,$0
        // Construct args vector for cgocallback().
        // By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9
        // args from the 5th on are on the stack.
        // In any case, even if function has 0,1,2,3,4 args, there is reserved
        // but uninitialized "shadow space" for the first 4 args.
        // The values are in registers.
-       MOVQ    CX, (24+0)(SP)
-       MOVQ    DX, (24+8)(SP)
-       MOVQ    R8, (24+16)(SP)
-       MOVQ    R9, (24+24)(SP)
-       // 6l does not accept writing POPQs here issuing a warning "unbalanced PUSH/POP"
-       MOVQ    0(SP), DX       // POPQ DX
-       MOVQ    8(SP), AX       // POPQ AX
-       ADDQ    $16, SP
+       MOVQ    CX, (16+0)(SP)
+       MOVQ    DX, (16+8)(SP)
+       MOVQ    R8, (16+16)(SP)
+       MOVQ    R9, (16+24)(SP)
+
+       // remove return address from stack, we are not returning there
+       MOVQ    0(SP), AX
+       ADDQ    $8, SP
+
+       // determine index into runtime·cbctxts table
+       SUBQ    $runtime·callbackasm(SB), AX
+       MOVQ    $0, DX
+       MOVQ    $5, CX  // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
+       DIVL    CX,
+
+       // find correspondent runtime·cbctxts table entry
+       MOVQ    runtime·cbctxts(SB), CX
+       MOVQ    -8(CX)(AX*8), AX
+
+       // extract callback context
+       MOVQ    cbctxt_argsize(AX), DX
+       MOVQ    cbctxt_gobody(AX), AX
 
        // preserve whatever's at the memory location that
        // the callback will use to store the return value
@@ -231,8 +236,6 @@ TEXT runtime·callbackasm(SB),7,$0
 
        // DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
        // as required by windows callback convention.
-       // 6l does not allow writing many PUSHQs here issuing a warning "nosplit stack overflow"
-       // the warning has no sense as this code uses os thread stack
        PUSHFQ
        SUBQ    $64, SP
        MOVQ    DI, 56(SP)
@@ -247,18 +250,17 @@ TEXT runtime·callbackasm(SB),7,$0
        // prepare call stack.  use SUBQ to hide from stack frame checks
        // cgocallback(Go func, void *frame, uintptr framesize)
        SUBQ    $24, SP
-       MOVQ    DX, 16(SP)      // uintptr framesize
-       MOVQ    CX, 8(SP)   // void *frame
-       MOVQ    AX, 0(SP)    // Go func
+       MOVQ    DX, 16(SP)      // argsize (including return value)
+       MOVQ    CX, 8(SP)       // callback parameters
+       MOVQ    AX, 0(SP)       // address of target Go function
        CLD
-       CALL  runtime·cgocallback_gofunc(SB)
+       CALL    runtime·cgocallback_gofunc(SB)
        MOVQ    0(SP), AX
        MOVQ    8(SP), CX
        MOVQ    16(SP), DX
        ADDQ    $24, SP
 
        // restore registers as required for windows callback
-       // 6l does not allow writing many POPs here issuing a warning "nosplit stack overflow"
        MOVQ    0(SP), R15
        MOVQ    8(SP), R14
        MOVQ    16(SP), R13