// 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
+*/
+
+#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)
+ throw("bad closure size");
+
+ ret = (byte**)((byte*)&arg0 + siz);
+
+ if(siz > 100) {
+ // TODO(kaib): implement stack growth preamble?
+ 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 = mal(n);
+ *ret = p;
+ q = p + n - siz;
+
+ pc = (uint32*)p;
+
+ // MOVW.W R14,-frame(R13)
+ *pc++ = 0xe52de000 | (siz + 4);
+
+ if(siz > 0) {
+ 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)
+ throw("bad math in sys.closure");
+}
+