blob: 3aca3a42d4632e60a9d7164d1a4f7e52d153acae [file] [log] [blame]
// 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);
}