diff options
Diffstat (limited to 'src/pkg/runtime/386/closure.c')
-rw-r--r-- | src/pkg/runtime/386/closure.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/pkg/runtime/386/closure.c b/src/pkg/runtime/386/closure.c new file mode 100644 index 000000000..6ccbe3b8b --- /dev/null +++ b/src/pkg/runtime/386/closure.c @@ -0,0 +1,104 @@ +// 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" + +#pragma textflag 7 +// func closure(siz int32, +// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, +// arg0, arg1, arg2 *ptr) (func(xxx) yyy) +void +sys·closure(int32 siz, byte *fn, byte *arg0) +{ + byte *p, *q, **ret; + int32 i, n; + int32 pcrel; + + if(siz < 0 || siz%4 != 0) + throw("bad closure size"); + + ret = (byte**)((byte*)&arg0 + siz); + + if(siz > 100) { + // TODO(rsc): implement stack growth preamble? + throw("closure too big"); + } + + // compute size of new fn. + // must match code laid out below. + n = 6+5+2+1; // SUBL MOVL MOVL CLD + if(siz <= 4*4) + n += 1*siz/4; // MOVSL MOVSL... + else + n += 6+2; // MOVL REP MOVSL + n += 5; // CALL + n += 6+1; // ADDL RET + + // store args aligned after code, so gc can find them. + n += siz; + if(n%4) + n += 4 - n%4; + + p = mal(n); + *ret = p; + q = p + n - siz; + mcpy(q, (byte*)&arg0, siz); + + // SUBL $siz, SP + *p++ = 0x81; + *p++ = 0xec; + *(uint32*)p = siz; + p += 4; + + // MOVL $q, SI + *p++ = 0xbe; + *(byte**)p = q; + p += 4; + + // MOVL SP, DI + *p++ = 0x89; + *p++ = 0xe7; + + // CLD + *p++ = 0xfc; + + if(siz <= 4*4) { + for(i=0; i<siz; i+=4) { + // MOVSL + *p++ = 0xa5; + } + } else { + // MOVL $(siz/4), CX [32-bit immediate siz/4] + *p++ = 0xc7; + *p++ = 0xc1; + *(uint32*)p = siz/4; + p += 4; + + // REP; MOVSL + *p++ = 0xf3; + *p++ = 0xa5; + } + + // call fn + pcrel = fn - (p+5); + // direct call with pc-relative offset + // CALL fn + *p++ = 0xe8; + *(int32*)p = pcrel; + p += 4; + + // ADDL $siz, SP + *p++ = 0x81; + *p++ = 0xc4; + *(uint32*)p = siz; + p += 4; + + // RET + *p++ = 0xc3; + + if(p > q) + throw("bad math in sys.closure"); +} + + |