runtime: aggregate defer allocations

benchmark             old ns/op    new ns/op    delta
BenchmarkDefer              165          113  -31.52%
BenchmarkDefer10            155          103  -33.55%
BenchmarkDeferMany          216          158  -26.85%

benchmark            old allocs   new allocs    delta
BenchmarkDefer                1            0  -100.00%
BenchmarkDefer10              1            0  -100.00%
BenchmarkDeferMany            1            0  -100.00%

benchmark             old bytes    new bytes    delta
BenchmarkDefer               64            0  -100.00%
BenchmarkDefer10             64            0  -100.00%
BenchmarkDeferMany           64           66    3.12%

Fixes #2364.

R=ken2
CC=golang-dev
https://golang.org/cl/7001051
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 6c9d50e..0c941f8 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -68,6 +68,7 @@
 typedef	struct	ChanType		ChanType;
 typedef	struct	MapType		MapType;
 typedef	struct	Defer		Defer;
+typedef	struct	DeferChunk	DeferChunk;
 typedef	struct	Panic		Panic;
 typedef	struct	Hmap		Hmap;
 typedef	struct	Hchan		Hchan;
@@ -218,6 +219,8 @@
 	int32	sig;
 	int32	writenbuf;
 	byte*	writebuf;
+	DeferChunk	*dchunk;
+	DeferChunk	*dchunknext;
 	uintptr	sigcode0;
 	uintptr	sigcode1;
 	uintptr	sigpc;
@@ -518,7 +521,8 @@
 struct Defer
 {
 	int32	siz;
-	bool	nofree;
+	bool	special; // not part of defer frame
+	bool	free; // if special, free when done
 	byte*	argp;  // where args were copied from
 	byte*	pc;
 	byte*	fn;
@@ -526,6 +530,12 @@
 	void*	args[1];	// padded to actual size
 };
 
+struct DeferChunk
+{
+	DeferChunk	*prev;
+	uintptr	off;
+};
+
 /*
  * panics
  */