diff options
Diffstat (limited to 'src/pkg/runtime/arm/closure.c')
-rw-r--r-- | src/pkg/runtime/arm/closure.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/pkg/runtime/arm/closure.c b/src/pkg/runtime/arm/closure.c new file mode 100644 index 000000000..119e91b61 --- /dev/null +++ b/src/pkg/runtime/arm/closure.c @@ -0,0 +1,129 @@ +// 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 runtime·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·memmove(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); +} + |