runtime: convert print.c to Go

LGTM=iant
R=golang-codereviews, iant
CC=dvyukov, golang-codereviews, khr, r
https://golang.org/cl/135930043
diff --git a/src/pkg/runtime/alg.goc b/src/pkg/runtime/alg.goc
index aa0c79d..0c3b3cd 100644
--- a/src/pkg/runtime/alg.goc
+++ b/src/pkg/runtime/alg.goc
@@ -29,7 +29,7 @@
 		v = *(uint64*)a;
 		break;
 	}
-	runtime·printint_c(v);
+	runtime·printint(v);
 }
 
 void
@@ -126,7 +126,7 @@
 runtime·strprint(uintptr s, void *a)
 {
 	USED(s);
-	runtime·printstring_c(*(String*)a);
+	runtime·printstring(*(String*)a);
 }
 
 void
@@ -146,7 +146,7 @@
 runtime·interprint(uintptr s, void *a)
 {
 	USED(s);
-	runtime·printiface_c(*(Iface*)a);
+	runtime·printiface(*(Iface*)a);
 }
 
 void
@@ -166,7 +166,7 @@
 runtime·nilinterprint(uintptr s, void *a)
 {
 	USED(s);
-	runtime·printeface_c(*(Eface*)a);
+	runtime·printeface(*(Eface*)a);
 }
 
 void
diff --git a/src/pkg/runtime/mprof.go b/src/pkg/runtime/mprof.go
index 9ee37d0..78384f8 100644
--- a/src/pkg/runtime/mprof.go
+++ b/src/pkg/runtime/mprof.go
@@ -162,16 +162,14 @@
 
 	n := 0
 	if len(buf) > 0 {
-		gp.writebuf = &buf[0]
-		gp.writenbuf = int32(len(buf))
+		gp.writebuf = buf
 		goroutineheader(gp)
 		traceback(pc, sp, 0, gp)
 		if all {
 			tracebackothers(gp)
 		}
-		n = len(buf) - int(gp.writenbuf)
+		n = len(buf) - len(gp.writebuf)
 		gp.writebuf = nil
-		gp.writenbuf = 0
 	}
 
 	if all {
diff --git a/src/pkg/runtime/os_darwin.c b/src/pkg/runtime/os_darwin.c
index 84b69b0..c660fb8 100644
--- a/src/pkg/runtime/os_darwin.c
+++ b/src/pkg/runtime/os_darwin.c
@@ -150,7 +150,7 @@
 	runtime·prints("mach error ");
 	runtime·prints(fn);
 	runtime·prints(": ");
-	runtime·printint_c(r);
+	runtime·printint(r);
 	runtime·prints("\n");
 	runtime·throw("mach error");
 }
@@ -218,7 +218,7 @@
 		runtime·prints("send:\t");
 		for(i=0; i<h->msgh_size/sizeof(p[0]); i++){
 			runtime·prints(" ");
-			runtime·printpointer_c((void*)p[i]);
+			runtime·printpointer((void*)p[i]);
 			if(i%8 == 7)
 				runtime·prints("\n\t");
 		}
@@ -231,7 +231,7 @@
 	if(ret != 0){
 		if(DebugMach){
 			runtime·prints("mach_msg error ");
-			runtime·printint_c(ret);
+			runtime·printint(ret);
 			runtime·prints("\n");
 		}
 		return ret;
@@ -242,7 +242,7 @@
 		runtime·prints("recv:\t");
 		for(i=0; i<h->msgh_size/sizeof(p[0]); i++){
 			runtime·prints(" ");
-			runtime·printpointer_c((void*)p[i]);
+			runtime·printpointer((void*)p[i]);
 			if(i%8 == 7)
 				runtime·prints("\n\t");
 		}
@@ -253,9 +253,9 @@
 	if(h->msgh_id != id+Reply){
 		if(DebugMach){
 			runtime·prints("mach_msg reply id mismatch ");
-			runtime·printint_c(h->msgh_id);
+			runtime·printint(h->msgh_id);
 			runtime·prints(" != ");
-			runtime·printint_c(id+Reply);
+			runtime·printint(id+Reply);
 			runtime·prints("\n");
 		}
 		return -303;	// MIG_REPLY_MISMATCH
@@ -272,7 +272,7 @@
 	&& !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)){
 		if(DebugMach){
 			runtime·prints("mig result ");
-			runtime·printint_c(c->code);
+			runtime·printint(c->code);
 			runtime·prints("\n");
 		}
 		return c->code;
@@ -281,9 +281,9 @@
 	if(h->msgh_size != rxsize){
 		if(DebugMach){
 			runtime·prints("mach_msg reply size mismatch ");
-			runtime·printint_c(h->msgh_size);
+			runtime·printint(h->msgh_size);
 			runtime·prints(" != ");
-			runtime·printint_c(rxsize);
+			runtime·printint(rxsize);
 			runtime·prints("\n");
 		}
 		return -307;	// MIG_ARRAY_TOO_LARGE
diff --git a/src/pkg/runtime/os_dragonfly.c b/src/pkg/runtime/os_dragonfly.c
index 1132f57..65121d3 100644
--- a/src/pkg/runtime/os_dragonfly.c
+++ b/src/pkg/runtime/os_dragonfly.c
@@ -63,11 +63,11 @@
 		return;
 
 	runtime·prints("umtx_wait addr=");
-	runtime·printpointer_c(addr);
+	runtime·printpointer(addr);
 	runtime·prints(" val=");
-	runtime·printint_c(val);
+	runtime·printint(val);
 	runtime·prints(" ret=");
-	runtime·printint_c(ret);
+	runtime·printint(ret);
 	runtime·prints("\n");
 	*(int32*)0x1005 = 0x1005;
 }
diff --git a/src/pkg/runtime/os_freebsd.c b/src/pkg/runtime/os_freebsd.c
index 20f71c4..d360f67 100644
--- a/src/pkg/runtime/os_freebsd.c
+++ b/src/pkg/runtime/os_freebsd.c
@@ -64,11 +64,11 @@
 
 fail:
 	runtime·prints("umtx_wait addr=");
-	runtime·printpointer_c(addr);
+	runtime·printpointer(addr);
 	runtime·prints(" val=");
-	runtime·printint_c(val);
+	runtime·printint(val);
 	runtime·prints(" ret=");
-	runtime·printint_c(ret);
+	runtime·printint(ret);
 	runtime·prints("\n");
 	*(int32*)0x1005 = 0x1005;
 }
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index 00c780b..39027a7 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -390,8 +390,11 @@
 	switch(g->m->dying) {
 	case 0:
 		g->m->dying = 1;
-		if(g != nil)
-			g->writebuf = nil;
+		if(g != nil) {
+			g->writebuf.array = nil;
+			g->writebuf.len = 0;
+			g->writebuf.cap = 0;
+		}
 		runtime·xadd(&runtime·panicking, 1);
 		runtime·lock(&paniclk);
 		if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c
deleted file mode 100644
index 57dfdab..0000000
--- a/src/pkg/runtime/print.c
+++ /dev/null
@@ -1,443 +0,0 @@
-// 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"
-#include "type.h"
-#include "../../cmd/ld/textflag.h"
-
-//static Mutex debuglock;
-
-static void vprintf(int8*, byte*);
-
-// write to goroutine-local buffer if diverting output,
-// or else standard error.
-static void
-gwrite(void *v, intgo n)
-{
-	if(g == nil || g->writebuf == nil) {
-		runtime·write(2, v, n);
-		return;
-	}
-
-	if(g->writenbuf == 0)
-		return;
-
-	if(n > g->writenbuf)
-		n = g->writenbuf;
-	runtime·memmove(g->writebuf, v, n);
-	g->writebuf += n;
-	g->writenbuf -= n;
-}
-
-void
-runtime·dump(byte *p, int32 n)
-{
-	int32 i;
-
-	for(i=0; i<n; i++) {
-		runtime·printpointer_c((byte*)(p[i]>>4));
-		runtime·printpointer_c((byte*)(p[i]&0xf));
-		if((i&15) == 15)
-			runtime·prints("\n");
-		else
-			runtime·prints(" ");
-	}
-	if(n & 15)
-		runtime·prints("\n");
-}
-
-void
-runtime·prints(int8 *s)
-{
-	gwrite(s, runtime·findnull((byte*)s));
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·printf(int8 *s, ...)
-{
-	byte *arg;
-
-	arg = (byte*)(&s+1);
-	vprintf(s, arg);
-}
-
-#pragma textflag NOSPLIT
-int32
-runtime·snprintf(byte *buf, int32 n, int8 *s, ...)
-{
-	byte *arg;
-	int32 m;
-
-	arg = (byte*)(&s+1);
-	g->writebuf = buf;
-	g->writenbuf = n-1;
-	vprintf(s, arg);
-	*g->writebuf = '\0';
-	m = g->writebuf - buf;
-	g->writenbuf = 0;
-	g->writebuf = nil;
-	return m;
-}
-
-// Very simple printf.  Only for debugging prints.
-// Do not add to this without checking with Rob.
-static void
-vprintf(int8 *s, byte *base)
-{
-	int8 *p, *lp;
-	uintptr arg, siz;
-	byte *v;
-
-	//runtime·lock(&debuglock);
-
-	lp = p = s;
-	arg = (uintptr)base;
-	for(; *p; p++) {
-		if(*p != '%')
-			continue;
-		if(p > lp)
-			gwrite(lp, p-lp);
-		p++;
-		siz = 0;
-		switch(*p) {
-		case 't':
-		case 'c':
-			siz = 1;
-			break;
-		case 'd':	// 32-bit
-		case 'x':
-			arg = ROUND(arg, 4);
-			siz = 4;
-			break;
-		case 'D':	// 64-bit
-		case 'U':
-		case 'X':
-		case 'f':
-			arg = ROUND(arg, sizeof(uintreg));
-			siz = 8;
-			break;
-		case 'C':
-			arg = ROUND(arg, sizeof(uintreg));
-			siz = 16;
-			break;
-		case 'p':	// pointer-sized
-		case 's':
-			arg = ROUND(arg, sizeof(uintptr));
-			siz = sizeof(uintptr);
-			break;
-		case 'S':	// pointer-aligned but bigger
-			arg = ROUND(arg, sizeof(uintptr));
-			siz = sizeof(String);
-			break;
-		case 'a':	// pointer-aligned but bigger
-			arg = ROUND(arg, sizeof(uintptr));
-			siz = sizeof(Slice);
-			break;
-		case 'i':	// pointer-aligned but bigger
-		case 'e':
-			arg = ROUND(arg, sizeof(uintptr));
-			siz = sizeof(Eface);
-			break;
-		}
-		v = (byte*)arg;
-		switch(*p) {
-		case 'a':
-			runtime·printslice_c(*(Slice*)v);
-			break;
-		case 'c':
-			runtime·printbyte_c(*(int8*)v);
-			break;
-		case 'd':
-			runtime·printint_c(*(int32*)v);
-			break;
-		case 'D':
-			runtime·printint_c(*(int64*)v);
-			break;
-		case 'e':
-			runtime·printeface_c(*(Eface*)v);
-			break;
-		case 'f':
-			runtime·printfloat_c(*(float64*)v);
-			break;
-		case 'C':
-			runtime·printcomplex_c(*(Complex128*)v);
-			break;
-		case 'i':
-			runtime·printiface_c(*(Iface*)v);
-			break;
-		case 'p':
-			runtime·printpointer_c(*(void**)v);
-			break;
-		case 's':
-			runtime·prints(*(int8**)v);
-			break;
-		case 'S':
-			runtime·printstring_c(*(String*)v);
-			break;
-		case 't':
-			runtime·printbool_c(*(bool*)v);
-			break;
-		case 'U':
-			runtime·printuint_c(*(uint64*)v);
-			break;
-		case 'x':
-			runtime·printhex_c(*(uint32*)v);
-			break;
-		case 'X':
-			runtime·printhex_c(*(uint64*)v);
-			break;
-		}
-		arg += siz;
-		lp = p+1;
-	}
-	if(p > lp)
-		gwrite(lp, p-lp);
-
-	//runtime·unlock(&debuglock);
-}
-
-static void
-goprintf_m(void)
-{
-	// Can assume s has terminating NUL because only
-	// the Go compiler generates calls to runtime·goprintf, using
-	// string constants, and all the string constants have NULs.
-	vprintf(g->m->ptrarg[0], g->m->ptrarg[1]);
-	g->m->ptrarg[0] = nil;
-	g->m->ptrarg[1] = nil;
-}
-
-#pragma textflag NOSPLIT
-void
-runtime·goprintf(String s, ...)
-{
-	g->m->ptrarg[0] = s.str;
-	g->m->ptrarg[1] = (byte*)(&s+1);
-	runtime·onM(goprintf_m);
-}
-
-void
-runtime·printpc_c(void *p)
-{
-	runtime·prints("PC=");
-	runtime·printhex_c((uint64)runtime·getcallerpc(p));
-}
-
-void
-runtime·printbool_c(bool v)
-{
-	if(v) {
-		gwrite((byte*)"true", 4);
-		return;
-	}
-	gwrite((byte*)"false", 5);
-}
-
-void
-runtime·printbyte_c(int8 c)
-{
-	gwrite(&c, 1);
-}
-
-void
-runtime·printfloat_c(float64 v)
-{
-	byte buf[20];
-	int32 e, s, i, n;
-	float64 h;
-
-	if(ISNAN(v)) {
-		gwrite("NaN", 3);
-		return;
-	}
-	if(v == runtime·posinf) {
-		gwrite("+Inf", 4);
-		return;
-	}
-	if(v == runtime·neginf) {
-		gwrite("-Inf", 4);
-		return;
-	}
-
-	n = 7;	// digits printed
-	e = 0;	// exp
-	s = 0;	// sign
-	if(v == 0) {
-		if(1/v == runtime·neginf)
-			s = 1;
-	} else {
-		// sign
-		if(v < 0) {
-			v = -v;
-			s = 1;
-		}
-
-		// normalize
-		while(v >= 10) {
-			e++;
-			v /= 10;
-		}
-		while(v < 1) {
-			e--;
-			v *= 10;
-		}
-
-		// round
-		h = 5;
-		for(i=0; i<n; i++)
-			h /= 10;
-
-		v += h;
-		if(v >= 10) {
-			e++;
-			v /= 10;
-		}
-	}
-
-	// format +d.dddd+edd
-	buf[0] = '+';
-	if(s)
-		buf[0] = '-';
-	for(i=0; i<n; i++) {
-		s = v;
-		buf[i+2] = s+'0';
-		v -= s;
-		v *= 10.;
-	}
-	buf[1] = buf[2];
-	buf[2] = '.';
-
-	buf[n+2] = 'e';
-	buf[n+3] = '+';
-	if(e < 0) {
-		e = -e;
-		buf[n+3] = '-';
-	}
-
-	buf[n+4] = (e/100) + '0';
-	buf[n+5] = (e/10)%10 + '0';
-	buf[n+6] = (e%10) + '0';
-	gwrite(buf, n+7);
-}
-
-void
-runtime·printcomplex_c(Complex128 v)
-{
-	gwrite("(", 1);
-	runtime·printfloat_c(v.real);
-	runtime·printfloat_c(v.imag);
-	gwrite("i)", 2);
-}
-
-void
-runtime·printuint_c(uint64 v)
-{
-	byte buf[100];
-	int32 i;
-
-	for(i=nelem(buf)-1; i>0; i--) {
-		buf[i] = v%10 + '0';
-		if(v < 10)
-			break;
-		v = v/10;
-	}
-	gwrite(buf+i, nelem(buf)-i);
-}
-
-void
-runtime·printint_c(int64 v)
-{
-	if(v < 0) {
-		gwrite("-", 1);
-		v = -v;
-	}
-	runtime·printuint_c(v);
-}
-
-void
-runtime·printhex_c(uint64 v)
-{
-	static int8 *dig = "0123456789abcdef";
-	byte buf[100];
-	int32 i;
-
-	i=nelem(buf);
-	for(; v>0; v/=16)
-		buf[--i] = dig[v%16];
-	if(i == nelem(buf))
-		buf[--i] = '0';
-	buf[--i] = 'x';
-	buf[--i] = '0';
-	gwrite(buf+i, nelem(buf)-i);
-}
-
-void
-runtime·printpointer_c(void *p)
-{
-	runtime·printhex_c((uintptr)p);
-}
-
-void
-runtime·printstring_c(String v)
-{
-	if(v.len > runtime·maxstring) {
-		gwrite("[string too long]", 17);
-		return;
-	}
-	if(v.len > 0)
-		gwrite(v.str, v.len);
-}
-
-void
-runtime·printslice_c(Slice s)
-{
-	runtime·prints("[");
-	runtime·printint_c(s.len);
-	runtime·prints("/");
-	runtime·printint_c(s.cap);
-	runtime·prints("]");
-	runtime·printpointer_c(s.array);
-}
-
-void
-runtime·printeface_c(Eface e)
-{
-	runtime·printf("(%p,%p)", e.type, e.data);
-}
-
-void
-runtime·printiface_c(Iface i)
-{
-	runtime·printf("(%p,%p)", i.tab, i.data);
-}
-
-void
-runtime·printstring_m(void)
-{
-	String s;
-
-	s.str = g->m->ptrarg[0];
-	g->m->ptrarg[0] = nil;
-	s.len = g->m->scalararg[0];
-	runtime·printstring_c(s);
-}
-
-void
-runtime·printuint_m(void)
-{
-	runtime·printuint_c(*(uint64*)(&g->m->scalararg[0]));
-}
-
-void
-runtime·printhex_m(void)
-{
-	runtime·printhex_c(g->m->scalararg[0]);
-}
-
-void
-runtime·printfloat_m(void)
-{
-	runtime·printfloat_c(*(float64*)(&g->m->scalararg[0]));
-}
diff --git a/src/pkg/runtime/print.go b/src/pkg/runtime/print.go
deleted file mode 100644
index fd79bc8..0000000
--- a/src/pkg/runtime/print.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2014 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.
-
-package runtime
-
-import (
-	"unsafe"
-)
-
-// these 4 functions are complicated enough that we will share
-// the print logic with the C printf.
-var (
-	printstring_m,
-	printuint_m,
-	printhex_m,
-	printfloat_m mFunction
-)
-
-func printstring(s string) {
-	mp := acquirem()
-	mp.scalararg[0] = uintptr(len(s))
-	mp.ptrarg[0] = (*stringStruct)(unsafe.Pointer(&s)).str
-	onM(&printstring_m)
-	releasem(mp)
-}
-
-func printuint(x uint64) {
-	mp := acquirem()
-	*(*uint64)(unsafe.Pointer(&mp.scalararg[0])) = x
-	onM(&printuint_m)
-	releasem(mp)
-}
-
-func printhex(x uintptr) {
-	mp := acquirem()
-	mp.scalararg[0] = uintptr(x)
-	onM(&printhex_m)
-	releasem(mp)
-}
-
-func printfloat(x float64) {
-	mp := acquirem()
-	*(*float64)(unsafe.Pointer(&mp.scalararg[0])) = x
-	onM(&printfloat_m)
-	releasem(mp)
-}
-
-// all other print functions are expressible as combinations
-// of the above 4 functions.
-func printnl() {
-	printstring("\n")
-}
-
-func printsp() {
-	printstring(" ")
-}
-
-func printbool(b bool) {
-	if b {
-		printstring("true")
-	} else {
-		printstring("false")
-	}
-}
-
-func printpointer(p unsafe.Pointer) {
-	printhex(uintptr(p))
-}
-
-func printint(x int64) {
-	if x < 0 {
-		printstring("-")
-		x = -x
-	}
-	printuint(uint64(x))
-}
-
-func printcomplex(x complex128) {
-	printstring("(")
-	printfloat(real(x))
-	printfloat(imag(x))
-	printstring("i)")
-}
-
-func printiface(i interface {
-	f()
-}) {
-	printstring("(")
-	printhex((*[2]uintptr)(unsafe.Pointer(&i))[0])
-	printstring(",")
-	printhex((*[2]uintptr)(unsafe.Pointer(&i))[1])
-	printstring(")")
-}
-
-func printeface(e interface{}) {
-	printstring("(")
-	printhex((*[2]uintptr)(unsafe.Pointer(&e))[0])
-	printstring(",")
-	printhex((*[2]uintptr)(unsafe.Pointer(&e))[1])
-	printstring(")")
-}
-
-func printslice(b []byte) {
-	printstring("[")
-	printint(int64(len(b)))
-	printstring("/")
-	printint(int64(cap(b)))
-	printstring("]")
-	printhex((*[3]uintptr)(unsafe.Pointer(&b))[0])
-}
diff --git a/src/pkg/runtime/print1.go b/src/pkg/runtime/print1.go
new file mode 100644
index 0000000..94ba9e4
--- /dev/null
+++ b/src/pkg/runtime/print1.go
@@ -0,0 +1,338 @@
+// 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.
+
+package runtime
+
+import "unsafe"
+
+//go:noescape
+func gostring(*byte) string
+
+func bytes(s string) (ret []byte) {
+	rp := (*slice)(unsafe.Pointer(&ret))
+	sp := (*_string)(noescape(unsafe.Pointer(&s)))
+	rp.array = sp.str
+	rp.len = uint(sp.len)
+	rp.cap = uint(sp.len)
+	return
+}
+
+// goprintf is the function call that is actually deferred when you write
+//	defer print(...)
+// It is otherwise unused. In particular it is not used for ordinary prints.
+// Right now a dynamically allocated string that is being passed as an
+// argument is invisible to the garbage collector and might be collected
+// if that argument list is the only reference. For now we ignore that possibility.
+// To fix, we should change to defer a call to vprintf with a pointer to
+// an argument list on the stack, stored in an appropriately typed
+// struct. golang.org/issue/8614.
+//go:nosplit
+func goprintf(s string) {
+	vprintf(s, add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
+}
+
+// printf is only called from C code. It has the same problem as goprintf
+// with strings possibly being collected from underneath.
+// However, the runtime never prints dynamically allocated
+// Go strings using printf. The strings it prints come from the symbol
+// and type tables.
+//go:nosplit
+func printf(s *byte) {
+	vprintf(gostring(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
+}
+
+// sprintf is only called from C code.
+// It has the same problem as goprintf.
+//go:nosplit
+func snprintf(dst *byte, n int32, s *byte) {
+	buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:0:n]
+
+	gp := getg()
+	gp.writebuf = buf[0:0 : n-1] // leave room for NUL, this is called from C
+	vprintf(gostring(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
+	buf[len(gp.writebuf)] = '\x00'
+	gp.writebuf = nil
+}
+
+//var debuglock mutex
+
+// write to goroutine-local buffer if diverting output,
+// or else standard error.
+func gwrite(b []byte) {
+	if len(b) == 0 {
+		return
+	}
+	gp := getg()
+	if gp == nil || gp.writebuf == nil {
+		write(2, unsafe.Pointer(&b[0]), int32(len(b)))
+		return
+	}
+
+	n := copy(gp.writebuf[len(gp.writebuf):cap(gp.writebuf)], b)
+	gp.writebuf = gp.writebuf[:len(gp.writebuf)+n]
+}
+
+func prints(s *byte) {
+	b := (*[1 << 30]byte)(unsafe.Pointer(s))
+	for i := 0; ; i++ {
+		if b[i] == 0 {
+			gwrite(b[:i])
+			return
+		}
+	}
+}
+
+func printsp() {
+	print(" ")
+}
+
+func printnl() {
+	print("\n")
+}
+
+// Very simple printf.  Only for debugging prints.
+// Do not add to this without checking with Rob.
+func vprintf(str string, arg unsafe.Pointer) {
+	//lock(&debuglock);
+
+	s := bytes(str)
+	start := 0
+	i := 0
+	for ; i < len(s); i++ {
+		if s[i] != '%' {
+			continue
+		}
+		if i > start {
+			gwrite(s[start:i])
+		}
+		if i++; i >= len(s) {
+			break
+		}
+		var siz uintptr
+		switch s[i] {
+		case 't', 'c':
+			siz = 1
+		case 'd', 'x': // 32-bit
+			arg = roundup(arg, 4)
+			siz = 4
+		case 'D', 'U', 'X', 'f': // 64-bit
+			arg = roundup(arg, unsafe.Sizeof(uintreg(0)))
+			siz = 8
+		case 'C':
+			arg = roundup(arg, unsafe.Sizeof(uintreg(0)))
+			siz = 16
+		case 'p', 's': // pointer-sized
+			arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+			siz = unsafe.Sizeof(uintptr(0))
+		case 'S': // pointer-aligned but bigger
+			arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+			siz = unsafe.Sizeof(string(""))
+		case 'a': // pointer-aligned but bigger
+			arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+			siz = unsafe.Sizeof([]byte{})
+		case 'i', 'e': // pointer-aligned but bigger
+			arg = roundup(arg, unsafe.Sizeof(uintptr(0)))
+			siz = unsafe.Sizeof(interface{}(nil))
+		}
+		switch s[i] {
+		case 'a':
+			printslice(*(*[]byte)(arg))
+		case 'c':
+			printbyte(*(*byte)(arg))
+		case 'd':
+			printint(int64(*(*int32)(arg)))
+		case 'D':
+			printint(int64(*(*int64)(arg)))
+		case 'e':
+			printeface(*(*interface{})(arg))
+		case 'f':
+			printfloat(*(*float64)(arg))
+		case 'C':
+			printcomplex(*(*complex128)(arg))
+		case 'i':
+			printiface(*(*fInterface)(arg))
+		case 'p':
+			printpointer(*(*unsafe.Pointer)(arg))
+		case 's':
+			prints(*(**byte)(arg))
+		case 'S':
+			printstring(*(*string)(arg))
+		case 't':
+			printbool(*(*bool)(arg))
+		case 'U':
+			printuint(*(*uint64)(arg))
+		case 'x':
+			printhex(uint64(*(*uint32)(arg)))
+		case 'X':
+			printhex(*(*uint64)(arg))
+		}
+		arg = add(arg, siz)
+		start = i + 1
+	}
+	if start < i {
+		gwrite(s[start:i])
+	}
+
+	//unlock(&debuglock);
+}
+
+func printpc(p unsafe.Pointer) {
+	print("PC=")
+	printhex(uint64(getcallerpc(p)))
+}
+
+func printbool(v bool) {
+	if v {
+		print("true")
+	} else {
+		print("false")
+	}
+}
+
+func printbyte(c byte) {
+	gwrite((*[1]byte)(unsafe.Pointer(&c))[:])
+}
+
+func printfloat(v float64) {
+	switch {
+	case v != v:
+		print("NaN")
+		return
+	case v+v == v && v > 0:
+		print("+Inf")
+		return
+	case v+v == v && v < 0:
+		print("-Inf")
+		return
+	}
+
+	const n = 7 // digits printed
+	var buf [n + 7]byte
+	buf[0] = '+'
+	e := 0 // exp
+	if v == 0 {
+		if 1/v < 0 {
+			buf[0] = '-'
+		}
+	} else {
+		if v < 0 {
+			v = -v
+			buf[0] = '-'
+		}
+
+		// normalize
+		for v >= 10 {
+			e++
+			v /= 10
+		}
+		for v < 1 {
+			e--
+			v *= 10
+		}
+
+		// round
+		h := 5.0
+		for i := 0; i < n; i++ {
+			h /= 10
+		}
+		v += h
+		if v >= 10 {
+			e++
+			v /= 10
+		}
+	}
+
+	// format +d.dddd+edd
+	for i := 0; i < n; i++ {
+		s := int(v)
+		buf[i+2] = byte(s + '0')
+		v -= float64(s)
+		v *= 10
+	}
+	buf[1] = buf[2]
+	buf[2] = '.'
+
+	buf[n+2] = 'e'
+	buf[n+3] = '+'
+	if e < 0 {
+		e = -e
+		buf[n+3] = '-'
+	}
+
+	buf[n+4] = byte(e/100) + '0'
+	buf[n+5] = byte(e/10)%10 + '0'
+	buf[n+6] = byte(e%10) + '0'
+	gwrite(buf[:])
+}
+
+func printcomplex(c complex128) {
+	print("(", real(c), imag(c), "i)")
+}
+
+func printuint(v uint64) {
+	var buf [100]byte
+	i := len(buf)
+	for i--; i > 0; i-- {
+		buf[i] = byte(v%10 + '0')
+		if v < 10 {
+			break
+		}
+		v /= 10
+	}
+	gwrite(buf[i:])
+}
+
+func printint(v int64) {
+	if v < 0 {
+		print("-")
+		v = -v
+	}
+	printuint(uint64(v))
+}
+
+func printhex(v uint64) {
+	const dig = "0123456789abcdef"
+	var buf [100]byte
+	i := len(buf)
+	for i--; i > 0; i-- {
+		buf[i] = dig[v%16]
+		if v < 16 {
+			break
+		}
+		v /= 16
+	}
+	i--
+	buf[i] = 'x'
+	i--
+	buf[i] = '0'
+	gwrite(buf[i:])
+}
+
+func printpointer(p unsafe.Pointer) {
+	printhex(uint64(uintptr(p)))
+}
+
+func printstring(s string) {
+	if uintptr(len(s)) > maxstring {
+		gwrite(bytes("[string too long]"))
+		return
+	}
+	gwrite(bytes(s))
+}
+
+func printslice(s []byte) {
+	sp := (*slice)(unsafe.Pointer(&s))
+	print("[", len(s), "/", cap(s), "]")
+	printpointer(unsafe.Pointer(sp.array))
+}
+
+func printeface(e interface{}) {
+	ep := (*eface)(unsafe.Pointer(&e))
+	print("(", ep._type, ",", ep.data, ")")
+}
+
+func printiface(i fInterface) {
+	ip := (*iface)(unsafe.Pointer(&i))
+	print("(", ip.tab, ",", ip.data, ")")
+}
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 8263202..1949283 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -1648,8 +1648,9 @@
 	gp->paniconfault = 0;
 	gp->defer = nil; // should be true already but just in case.
 	gp->panic = nil; // non-nil for Goexit during panic. points at stack-allocated data.
-	gp->writenbuf = 0;
-	gp->writebuf = nil;
+	gp->writebuf.array = nil;
+	gp->writebuf.len = 0;
+	gp->writebuf.cap = 0;
 	gp->waitreason.str = nil;
 	gp->waitreason.len = 0;
 	gp->param = nil;
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 90cb3cb..4dfc4f2 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -297,7 +297,7 @@
 	M*	lockedm;
 	int32	sig;
 	int32	writenbuf;
-	byte*	writebuf;
+	Slice	writebuf;
 	uintptr	sigcode0;
 	uintptr	sigcode1;
 	uintptr	sigpc;
@@ -828,7 +828,7 @@
 bool	runtime·canpanic(G*);
 void	runtime·prints(int8*);
 void	runtime·printf(int8*, ...);
-int32	runtime·snprintf(byte*, int32, int8*, ...);
+void	runtime·snprintf(byte*, int32, int8*, ...);
 byte*	runtime·mchr(byte*, byte, byte*);
 int32	runtime·mcmp(byte*, byte*, uintptr);
 void	runtime·memmove(void*, void*, uintptr);
@@ -1063,19 +1063,19 @@
 void	runtime·memclr(byte*, uintptr);
 void	runtime·setcallerpc(void*, void*);
 void*	runtime·getcallerpc(void*);
-void	runtime·printbool_c(bool);
-void	runtime·printbyte_c(int8);
-void	runtime·printfloat_c(float64);
-void	runtime·printint_c(int64);
-void	runtime·printiface_c(Iface);
-void	runtime·printeface_c(Eface);
-void	runtime·printstring_c(String);
-void	runtime·printpc_c(void*);
-void	runtime·printpointer_c(void*);
-void	runtime·printuint_c(uint64);
-void	runtime·printhex_c(uint64);
-void	runtime·printslice_c(Slice);
-void	runtime·printcomplex_c(Complex128);
+void	runtime·printbool(bool);
+void	runtime·printbyte(int8);
+void	runtime·printfloat(float64);
+void	runtime·printint(int64);
+void	runtime·printiface(Iface);
+void	runtime·printeface(Eface);
+void	runtime·printstring(String);
+void	runtime·printpc(void*);
+void	runtime·printpointer(void*);
+void	runtime·printuint(uint64);
+void	runtime·printhex(uint64);
+void	runtime·printslice(Slice);
+void	runtime·printcomplex(Complex128);
 
 /*
  * runtime go-called
diff --git a/src/pkg/runtime/stubs.go b/src/pkg/runtime/stubs.go
index f69a041..26126fc 100644
--- a/src/pkg/runtime/stubs.go
+++ b/src/pkg/runtime/stubs.go
@@ -173,7 +173,6 @@
 func gosave(buf *gobuf)
 func open(name *byte, mode, perm int32) int32
 func read(fd int32, p unsafe.Pointer, n int32) int32
-func write(fd uintptr, p unsafe.Pointer, n int32) int32
 func close(fd int32) int32
 func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32
 func jmpdefer(fv *funcval, argp unsafe.Pointer)
@@ -206,6 +205,9 @@
 func purgecachedstats(c *mcache)
 
 //go:noescape
+func write(fd uintptr, p unsafe.Pointer, n int32) int32
+
+//go:noescape
 func cas(ptr *uint32, old, new uint32) bool
 
 //go:noescape
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index 0ae40a6..5e937cb 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -234,7 +234,7 @@
 					}
 					if(i != 0)
 						runtime·prints(", ");
-					runtime·printhex_c(((uintptr*)frame.argp)[i]);
+					runtime·printhex(((uintptr*)frame.argp)[i]);
 				}
 				runtime·prints(")\n");
 				line = runtime·funcline(f, tracepc, &file);
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index 7c76daf..f3ba702 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -270,7 +270,7 @@
 					}
 					if(i != 0)
 						runtime·prints(", ");
-					runtime·printhex_c(((uintptr*)frame.argp)[i]);
+					runtime·printhex(((uintptr*)frame.argp)[i]);
 				}
 				runtime·prints(")\n");
 				line = runtime·funcline(f, tracepc, &file);