| // 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" |
| |
| /* |
| There are two bits of magic: |
| - The signature of the compiler generated function uses two stack frames |
| as arguments (callerpc separates these frames) |
| - size determines how many arguments runtime.closure actually has |
| starting at arg0. |
| |
| Example closure with 3 captured variables: |
| func closure(siz int32, |
| fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, |
| arg0, arg1, arg2 *ptr) (func(xxx) yyy) |
| |
| Code generated: |
| src R0 |
| dst R1 |
| end R3 |
| tmp R4 |
| frame = siz+4 |
| |
| //skip loop for 0 size closures |
| MOVW.W R14,-frame(R13) |
| |
| MOVW $vars(PC), R0 |
| MOVW $4(SP), R1 |
| MOVW $siz(R0), R3 |
| loop: MOVW.P 4(R0), R4 |
| MOVW.P R4, 4(R1) |
| CMP R0, R3 |
| BNE loop |
| |
| MOVW 8(PC), R0 |
| BL (R0) // 2 words |
| MOVW.P frame(R13),R15 |
| fptr: WORD *fn |
| vars: WORD arg0 |
| WORD arg1 |
| WORD arg2 |
| */ |
| |
| extern void cacheflush(byte* start, byte* end); |
| |
| #pragma textflag 7 |
| void |
| runtime·closure(int32 siz, byte *fn, byte *arg0) |
| { |
| byte *p, *q, **ret; |
| uint32 *pc; |
| int32 n; |
| |
| if(siz < 0 || siz%4 != 0) |
| runtime·throw("bad closure size"); |
| |
| ret = (byte**)((byte*)&arg0 + siz); |
| |
| if(siz > 100) { |
| // TODO(kaib): implement stack growth preamble? |
| runtime·throw("closure too big"); |
| } |
| |
| // size of new fn. |
| // must match code laid out below. |
| if (siz > 0) |
| n = 6 * 4 + 7 * 4; |
| else |
| n = 6 * 4; |
| |
| // store args aligned after code, so gc can find them. |
| n += siz; |
| |
| p = runtime·mal(n); |
| *ret = p; |
| q = p + n - siz; |
| |
| pc = (uint32*)p; |
| |
| // MOVW.W R14,-frame(R13) |
| *pc++ = 0xe52de000 | (siz + 4); |
| |
| if(siz > 0) { |
| runtime·mcpy(q, (byte*)&arg0, siz); |
| |
| // MOVW $vars(PC), R0 |
| *pc = 0xe28f0000 | (int32)(q - (byte*)pc - 8); |
| pc++; |
| |
| // MOVW $4(SP), R1 |
| *pc++ = 0xe28d1004; |
| |
| // MOVW $siz(R0), R3 |
| *pc++ = 0xe2803000 | siz; |
| |
| // MOVW.P 4(R0), R4 |
| *pc++ = 0xe4904004; |
| // MOVW.P R4, 4(R1) |
| *pc++ = 0xe4814004; |
| // CMP R0, R3 |
| *pc++ = 0xe1530000; |
| // BNE loop |
| *pc++ = 0x1afffffb; |
| } |
| |
| // MOVW fptr(PC), R0 |
| *pc = 0xe59f0008 | (int32)((q - 4) -(byte*) pc - 8); |
| pc++; |
| |
| // BL (R0) |
| *pc++ = 0xe28fe000; |
| *pc++ = 0xe280f000; |
| |
| // MOVW.P frame(R13),R15 |
| *pc++ = 0xe49df000 | (siz + 4); |
| |
| // WORD *fn |
| *pc++ = (uint32)fn; |
| |
| p = (byte*)pc; |
| |
| if(p > q) |
| runtime·throw("bad math in sys.closure"); |
| |
| runtime·cacheflush(*ret, q+siz); |
| } |
| |