Patchwork libgo patch committed: Update to current sources

login
register
mail settings
Submitter Ian Taylor
Date Jan. 29, 2013, 8:53 p.m.
Message ID <mcr1ud3ww0t.fsf@google.com>
Download mbox | patch
Permalink /patch/216651/
State New
Headers show

Comments

Ian Taylor - Jan. 29, 2013, 8:53 p.m.
I've updated libgo to the current sources, in an attempt to get GCC 4.8
closer to the upcoming Go 1.1 release.  I've attached the patches to the
directories that are mainly different for gccgo.  Bootstrapped and ran
Go testsuite on x86_64-unknown-linux-gnu.  Committed to mainline.

Ian

Patch

diff -r 03c4cdef61a5 libgo/MERGE
--- a/libgo/MERGE	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/MERGE	Tue Jan 29 10:54:22 2013 -0800
@@ -1,4 +1,4 @@ 
-6fdc1974457c
+921e53d4863c
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
diff -r 03c4cdef61a5 libgo/Makefile.am
--- a/libgo/Makefile.am	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/Makefile.am	Tue Jan 29 10:54:22 2013 -0800
@@ -227,8 +227,8 @@ 
 	$(exp_inotify_gox) \
 	exp/norm.gox \
 	exp/proxy.gox \
+	exp/ssa.gox \
 	exp/terminal.gox \
-	exp/types.gox \
 	exp/utf8string.gox
 
 toolexeclibgoexphtmldir = $(toolexeclibgoexpdir)/html
@@ -256,7 +256,8 @@ 
 	go/parser.gox \
 	go/printer.gox \
 	go/scanner.gox \
-	go/token.gox
+	go/token.gox \
+	go/types.gox
 
 toolexeclibgohashdir = $(toolexeclibgodir)/hash
 
@@ -682,7 +683,7 @@ 
 go_net_newpollserver_file = go/net/newpollserver_unix.go
 else # !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
 if LIBGO_IS_NETBSD
-go_net_fd_os_file = go/net/fd_netbsd.go
+go_net_fd_os_file = go/net/fd_bsd.go
 go_net_newpollserver_file = go/net/newpollserver_unix.go
 else # !LIBGO_IS_NETBSD && !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
 # By default use select with pipes.  Most systems should have
@@ -753,9 +754,16 @@ 
 endif
 endif
 
+if LIBGO_IS_LINUX
+go_net_cloexec_file = go/net/sock_cloexec.go
+else
+go_net_cloexec_file = go/net/sys_cloexec.go
+endif
+
 go_net_files = \
 	go/net/cgo_unix.go \
 	$(go_net_cgo_file) \
+	$(go_net_cloexec_file) \
 	go/net/dial.go \
 	go/net/dnsclient.go \
 	go/net/dnsclient_unix.go \
@@ -856,6 +864,12 @@ 
 endif
 endif
 
+if LIBGO_IS_LINUX
+go_os_pipe_file = go/os/pipe_linux.go
+else
+go_os_pipe_file = go/os/pipe_bsd.go
+endif
+
 go_os_files = \
 	$(go_os_dir_file) \
 	go/os/dir.go \
@@ -872,6 +886,7 @@ 
 	go/os/getwd.go \
 	go/os/path.go \
 	go/os/path_unix.go \
+	$(go_os_pipe_file) \
 	go/os/proc.go \
 	$(go_os_stat_file) \
 	go/os/str.go \
@@ -1026,6 +1041,7 @@ 
 go_compress_flate_files = \
 	go/compress/flate/copy.go \
 	go/compress/flate/deflate.go \
+	go/compress/flate/fixedhuff.go \
 	go/compress/flate/huffman_bit_writer.go \
 	go/compress/flate/huffman_code.go \
 	go/compress/flate/inflate.go \
@@ -1222,8 +1238,10 @@ 
 go_exp_locale_collate_files = \
 	go/exp/locale/collate/colelem.go \
 	go/exp/locale/collate/collate.go \
+	go/exp/locale/collate/colltab.go \
 	go/exp/locale/collate/contract.go \
 	go/exp/locale/collate/export.go \
+	go/exp/locale/collate/sort.go \
 	go/exp/locale/collate/table.go \
 	go/exp/locale/collate/tables.go \
 	go/exp/locale/collate/trie.go
@@ -1248,23 +1266,18 @@ 
 	go/exp/proxy/per_host.go \
 	go/exp/proxy/proxy.go \
 	go/exp/proxy/socks5.go
+go_exp_ssa_files = \
+	go/exp/ssa/blockopt.go \
+	go/exp/ssa/doc.go \
+	go/exp/ssa/func.go \
+	go/exp/ssa/sanity.go \
+	go/exp/ssa/ssa.go \
+	go/exp/ssa/literal.go \
+	go/exp/ssa/print.go \
+	go/exp/ssa/util.go
 go_exp_terminal_files = \
 	go/exp/terminal/terminal.go \
 	go/exp/terminal/util.go
-go_exp_types_files = \
-	go/exp/types/builtins.go \
-	go/exp/types/check.go \
-	go/exp/types/const.go \
-	go/exp/types/conversions.go \
-	go/exp/types/errors.go \
-	go/exp/types/exportdata.go \
-	go/exp/types/expr.go \
-	go/exp/types/gcimporter.go \
-	go/exp/types/operand.go \
-	go/exp/types/predicates.go \
-	go/exp/types/stmt.go \
-	go/exp/types/types.go \
-	go/exp/types/universe.go
 go_exp_utf8string_files = \
 	go/exp/utf8string/string.go
 
@@ -1305,6 +1318,24 @@ 
 	go/go/token/position.go \
 	go/go/token/serialize.go \
 	go/go/token/token.go
+go_go_types_files = \
+	go/go/types/api.go \
+	go/go/types/builtins.go \
+	go/go/types/check.go \
+	go/go/types/const.go \
+	go/go/types/conversions.go \
+	go/go/types/errors.go \
+	go/go/types/exportdata.go \
+	go/go/types/expr.go \
+	go/go/types/gcimporter.go \
+	go/go/types/objects.go \
+	go/go/types/operand.go \
+	go/go/types/predicates.go \
+	go/go/types/resolve.go \
+	go/go/types/scope.go \
+	go/go/types/stmt.go \
+	go/go/types/types.go \
+	go/go/types/universe.go
 
 go_hash_adler32_files = \
 	go/hash/adler32/adler32.go
@@ -1848,8 +1879,8 @@ 
 	exp/locale/collate/build.lo \
 	exp/norm.lo \
 	exp/proxy.lo \
+	exp/ssa.lo \
 	exp/terminal.lo \
-	exp/types.lo \
 	exp/utf8string.lo \
 	html/template.lo \
 	go/ast.lo \
@@ -1860,6 +1891,7 @@ 
 	go/printer.lo \
 	go/scanner.lo \
 	go/token.lo \
+	go/types.lo \
 	hash/adler32.lo \
 	hash/crc32.lo \
 	hash/crc64.lo \
@@ -2751,6 +2783,15 @@ 
 	@$(CHECK)
 .PHONY: exp/proxy/check
 
+@go_include@ exp/ssa.lo.dep
+exp/ssa.lo.dep: $(go_exp_ssa_files)
+	$(BUILDDEPS)
+exp/ssa.lo: $(go_exp_ssa_files)
+	$(BUILDPACKAGE)
+exp/ssa/check: $(CHECK_DEPS)
+	@$(CHECK)
+.PHONY: exp/ssa/check
+
 @go_include@ exp/terminal.lo.dep
 exp/terminal.lo.dep: $(go_exp_terminal_files)
 	$(BUILDDEPS)
@@ -2760,15 +2801,6 @@ 
 	@$(CHECK)
 .PHONY: exp/terminal/check
 
-@go_include@ exp/types.lo.dep
-exp/types.lo.dep: $(go_exp_types_files)
-	$(BUILDDEPS)
-exp/types.lo: $(go_exp_types_files)
-	$(BUILDPACKAGE)
-exp/types/check: $(CHECK_DEPS)
-	@$(CHECK)
-.PHONY: exp/types/check
-
 @go_include@ exp/utf8string.lo.dep
 exp/utf8string.lo.dep: $(go_exp_utf8string_files)
 	$(BUILDDEPS)
@@ -2877,6 +2909,15 @@ 
 	@$(CHECK)
 .PHONY: go/token/check
 
+@go_include@ go/types.lo.dep
+go/types.lo.dep: $(go_go_types_files)
+	$(BUILDDEPS)
+go/types.lo: $(go_go_types_files)
+	$(BUILDPACKAGE)
+go/types/check: $(CHECK_DEPS)
+	@$(CHECK)
+.PHONY: go/types/check
+
 @go_include@ hash/adler32.lo.dep
 hash/adler32.lo.dep: $(go_hash_adler32_files)
 	$(BUILDDEPS)
@@ -3507,10 +3548,10 @@ 
 	$(BUILDGOX)
 exp/proxy.gox: exp/proxy.lo
 	$(BUILDGOX)
+exp/ssa.gox: exp/ssa.lo
+	$(BUILDGOX)
 exp/terminal.gox: exp/terminal.lo
 	$(BUILDGOX)
-exp/types.gox: exp/types.lo
-	$(BUILDGOX)
 exp/utf8string.gox: exp/utf8string.lo	
 	$(BUILDGOX)
 
@@ -3533,6 +3574,8 @@ 
 	$(BUILDGOX)
 go/token.gox: go/token.lo
 	$(BUILDGOX)
+go/types.gox: go/types.lo
+	$(BUILDGOX)
 
 hash/adler32.gox: hash/adler32.lo
 	$(BUILDGOX)
@@ -3734,7 +3777,6 @@ 
 	exp/norm/check \
 	exp/proxy/check \
 	exp/terminal/check \
-	exp/types/check \
 	exp/utf8string/check \
 	html/template/check \
 	go/ast/check \
@@ -3745,7 +3787,7 @@ 
 	go/printer/check \
 	go/scanner/check \
 	go/token/check \
-	$(go_types_check_omitted_since_it_calls_6g) \
+	go/types/check \
 	hash/adler32/check \
 	hash/crc32/check \
 	hash/crc64/check \
diff -r 03c4cdef61a5 libgo/configure.ac
--- a/libgo/configure.ac	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/configure.ac	Tue Jan 29 10:54:22 2013 -0800
@@ -491,7 +491,7 @@ 
 AM_CONDITIONAL(HAVE_STRERROR_R, test "$ac_cv_func_strerror_r" = yes)
 AM_CONDITIONAL(HAVE_WAIT4, test "$ac_cv_func_wait4" = yes)
 
-AC_CHECK_FUNCS(epoll_create1 faccessat fallocate fchmodat fchownat futimesat inotify_add_watch inotify_init inotify_init1 inotify_rm_watch mkdirat mknodat openat renameat sync_file_range splice tee unlinkat unshare)
+AC_CHECK_FUNCS(accept4 epoll_create1 faccessat fallocate fchmodat fchownat futimesat inotify_add_watch inotify_init inotify_init1 inotify_rm_watch mkdirat mknodat openat pipe2 renameat sync_file_range splice tee unlinkat unshare utimensat)
 AC_TYPE_OFF_T
 AC_CHECK_TYPES([loff_t])
 
diff -r 03c4cdef61a5 libgo/go/syscall/exec_bsd.go
--- a/libgo/go/syscall/exec_bsd.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/exec_bsd.go	Tue Jan 29 10:54:22 2013 -0800
@@ -223,3 +223,17 @@ 
 	// and this shuts up the compiler.
 	panic("unreached")
 }
+
+// Try to open a pipe with O_CLOEXEC set on both file descriptors.
+func forkExecPipe(p []int) error {
+	err := Pipe(p)
+	if err != nil {
+		return err
+	}
+	_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
+	if err != nil {
+		return err
+	}
+	_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
+	return err
+}
diff -r 03c4cdef61a5 libgo/go/syscall/exec_linux.go
--- a/libgo/go/syscall/exec_linux.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/exec_linux.go	Tue Jan 29 10:54:22 2013 -0800
@@ -250,3 +250,20 @@ 
 	// and this shuts up the compiler.
 	panic("unreached")
 }
+
+// Try to open a pipe with O_CLOEXEC set on both file descriptors.
+func forkExecPipe(p []int) (err error) {
+	err = Pipe2(p, O_CLOEXEC)
+	// pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it
+	// might not be implemented.
+	if err == ENOSYS {
+		if err = Pipe(p); err != nil {
+			return
+		}
+		if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil {
+			return
+		}
+		_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
+	}
+	return
+}
diff -r 03c4cdef61a5 libgo/go/syscall/exec_unix.go
--- a/libgo/go/syscall/exec_unix.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/exec_unix.go	Tue Jan 29 10:54:22 2013 -0800
@@ -219,13 +219,7 @@ 
 	ForkLock.Lock()
 
 	// Allocate child status pipe close on exec.
-	if err = Pipe(p[0:]); err != nil {
-		goto error
-	}
-	if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil {
-		goto error
-	}
-	if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != nil {
+	if err = forkExecPipe(p[:]); err != nil {
 		goto error
 	}
 
diff -r 03c4cdef61a5 libgo/go/syscall/libcall_linux.go
--- a/libgo/go/syscall/libcall_linux.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/libcall_linux.go	Tue Jan 29 10:54:22 2013 -0800
@@ -166,6 +166,24 @@ 
 	return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "")
 }
 
+//sys	accept4(fd int, sa *RawSockaddrAny, len *Socklen_t, flags int) (nfd int, err error)
+//accept4(fd _C_int, sa *RawSockaddrAny, len *Socklen_t, flags _C_int) _C_int
+
+func Accept4(fd int, flags int) (nfd int, sa Sockaddr, err error) {
+	var rsa RawSockaddrAny
+	var len Socklen_t = SizeofSockaddrAny
+	nfd, err = accept4(fd, &rsa, &len, flags)
+	if err != nil {
+		return -1, nil, err
+	}
+	sa, err = anyToSockaddr(&rsa)
+	if err != nil {
+		Close(nfd)
+		return -1, nil, err
+	}
+	return nfd, sa, nil
+}
+
 //sys	Acct(path string) (err error)
 //acct(path *byte) int
 
@@ -271,6 +289,19 @@ 
 //sys	Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
 //mknodat(dirfd int, path *byte, mode Mode_t, dev _dev_t) int
 
+//sysnb	pipe2(p *[2]_C_int, flags int) (err error)
+//pipe2(p *[2]_C_int, flags _C_int) _C_int
+func Pipe2(p []int, flags int) (err error) {
+	if len(p) != 2 {
+		return EINVAL
+	}
+	var pp [2]_C_int
+	err = pipe2(&pp, flags)
+	p[0] = int(pp[0])
+	p[1] = int(pp[1])
+	return
+}
+
 //sys	PivotRoot(newroot string, putold string) (err error)
 //pivot_root(newroot *byte, putold *byte) int
 
diff -r 03c4cdef61a5 libgo/go/syscall/netlink_linux.go
--- a/libgo/go/syscall/netlink_linux.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/netlink_linux.go	Tue Jan 29 10:54:22 2013 -0800
@@ -6,9 +6,7 @@ 
 
 package syscall
 
-import (
-	"unsafe"
-)
+import "unsafe"
 
 // Round the length of a netlink message up to align it properly.
 func nlmAlignOf(msglen int) int {
@@ -21,8 +19,8 @@ 
 	return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
 }
 
-// NetlinkRouteRequest represents the request message to receive
-// routing and link states from the kernel.
+// NetlinkRouteRequest represents a request message to receive routing
+// and link states from the kernel.
 type NetlinkRouteRequest struct {
 	Header NlMsghdr
 	Data   RtGenmsg
@@ -49,167 +47,131 @@ 
 	return rr.toWireFormat()
 }
 
-// NetlinkRIB returns routing information base, as known as RIB,
-// which consists of network facility information, states and
-// parameters.
+// NetlinkRIB returns routing information base, as known as RIB, which
+// consists of network facility information, states and parameters.
 func NetlinkRIB(proto, family int) ([]byte, error) {
-	var (
-		lsanl SockaddrNetlink
-		tab   []byte
-	)
-
-	s, e := Socket(AF_NETLINK, SOCK_RAW, 0)
-	if e != nil {
-		return nil, e
+	s, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
+	if err != nil {
+		return nil, err
 	}
 	defer Close(s)
-
-	lsanl.Family = AF_NETLINK
-	e = Bind(s, &lsanl)
-	if e != nil {
-		return nil, e
+	lsa := &SockaddrNetlink{Family: AF_NETLINK}
+	if err := Bind(s, lsa); err != nil {
+		return nil, err
 	}
-
-	seq := 1
-	wb := newNetlinkRouteRequest(proto, seq, family)
-	e = Sendto(s, wb, 0, &lsanl)
-	if e != nil {
-		return nil, e
+	wb := newNetlinkRouteRequest(proto, 1, family)
+	if err := Sendto(s, wb, 0, lsa); err != nil {
+		return nil, err
 	}
-
+	var tab []byte
+done:
 	for {
-		var (
-			rb  []byte
-			nr  int
-			lsa Sockaddr
-		)
-
-		rb = make([]byte, Getpagesize())
-		nr, _, e = Recvfrom(s, rb, 0)
-		if e != nil {
-			return nil, e
+		rb := make([]byte, Getpagesize())
+		nr, _, err := Recvfrom(s, rb, 0)
+		if err != nil {
+			return nil, err
 		}
 		if nr < NLMSG_HDRLEN {
 			return nil, EINVAL
 		}
 		rb = rb[:nr]
 		tab = append(tab, rb...)
-
-		msgs, _ := ParseNetlinkMessage(rb)
+		msgs, err := ParseNetlinkMessage(rb)
+		if err != nil {
+			return nil, err
+		}
 		for _, m := range msgs {
-			if lsa, e = Getsockname(s); e != nil {
-				return nil, e
+			lsa, err := Getsockname(s)
+			if err != nil {
+				return nil, err
 			}
 			switch v := lsa.(type) {
 			case *SockaddrNetlink:
-				if m.Header.Seq != uint32(seq) || m.Header.Pid != v.Pid {
+				if m.Header.Seq != 1 || m.Header.Pid != v.Pid {
 					return nil, EINVAL
 				}
 			default:
 				return nil, EINVAL
 			}
 			if m.Header.Type == NLMSG_DONE {
-				goto done
+				break done
 			}
 			if m.Header.Type == NLMSG_ERROR {
 				return nil, EINVAL
 			}
 		}
 	}
-
-done:
 	return tab, nil
 }
 
-// NetlinkMessage represents the netlink message.
+// NetlinkMessage represents a netlink message.
 type NetlinkMessage struct {
 	Header NlMsghdr
 	Data   []byte
 }
 
-// ParseNetlinkMessage parses buf as netlink messages and returns
-// the slice containing the NetlinkMessage structs.
-func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) {
-	var (
-		h    *NlMsghdr
-		dbuf []byte
-		dlen int
-		e    error
-		msgs []NetlinkMessage
-	)
-
-	for len(buf) >= NLMSG_HDRLEN {
-		h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf)
-		if e != nil {
-			break
+// ParseNetlinkMessage parses b as an array of netlink messages and
+// returns the slice containing the NetlinkMessage structures.
+func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
+	var msgs []NetlinkMessage
+	for len(b) >= NLMSG_HDRLEN {
+		h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)
+		if err != nil {
+			return nil, err
 		}
-		m := NetlinkMessage{}
-		m.Header = *h
-		m.Data = dbuf[:int(h.Len)-NLMSG_HDRLEN]
+		m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}
 		msgs = append(msgs, m)
-		buf = buf[dlen:]
+		b = b[dlen:]
 	}
-
-	return msgs, e
+	return msgs, nil
 }
 
-func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) {
-	h := (*NlMsghdr)(unsafe.Pointer(&buf[0]))
-	if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) {
+func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {
+	h := (*NlMsghdr)(unsafe.Pointer(&b[0]))
+	if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {
 		return nil, nil, 0, EINVAL
 	}
-	return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
+	return h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
 }
 
-// NetlinkRouteAttr represents the netlink route attribute.
+// NetlinkRouteAttr represents a netlink route attribute.
 type NetlinkRouteAttr struct {
 	Attr  RtAttr
 	Value []byte
 }
 
-// ParseNetlinkRouteAttr parses msg's payload as netlink route
-// attributes and returns the slice containing the NetlinkRouteAttr
-// structs.
-func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) {
-	var (
-		buf   []byte
-		a     *RtAttr
-		alen  int
-		vbuf  []byte
-		e     error
-		attrs []NetlinkRouteAttr
-	)
-
-	switch msg.Header.Type {
+// ParseNetlinkRouteAttr parses m's payload as an array of netlink
+// route attributes and returns the slice containing the
+// NetlinkRouteAttr structures.
+func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {
+	var b []byte
+	switch m.Header.Type {
 	case RTM_NEWLINK, RTM_DELLINK:
-		buf = msg.Data[SizeofIfInfomsg:]
+		b = m.Data[SizeofIfInfomsg:]
 	case RTM_NEWADDR, RTM_DELADDR:
-		buf = msg.Data[SizeofIfAddrmsg:]
+		b = m.Data[SizeofIfAddrmsg:]
 	case RTM_NEWROUTE, RTM_DELROUTE:
-		buf = msg.Data[SizeofRtMsg:]
+		b = m.Data[SizeofRtMsg:]
 	default:
 		return nil, EINVAL
 	}
-
-	for len(buf) >= SizeofRtAttr {
-		a, vbuf, alen, e = netlinkRouteAttrAndValue(buf)
-		if e != nil {
-			break
+	var attrs []NetlinkRouteAttr
+	for len(b) >= SizeofRtAttr {
+		a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
+		if err != nil {
+			return nil, err
 		}
-		ra := NetlinkRouteAttr{}
-		ra.Attr = *a
-		ra.Value = vbuf[:int(a.Len)-SizeofRtAttr]
+		ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}
 		attrs = append(attrs, ra)
-		buf = buf[alen:]
+		b = b[alen:]
 	}
-
 	return attrs, nil
 }
 
-func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) {
-	h := (*RtAttr)(unsafe.Pointer(&buf[0]))
-	if int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) {
+func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {
+	a := (*RtAttr)(unsafe.Pointer(&b[0]))
+	if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {
 		return nil, nil, 0, EINVAL
 	}
-	return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil
+	return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
 }
diff -r 03c4cdef61a5 libgo/go/syscall/sockcmsg_linux.go
--- a/libgo/go/syscall/sockcmsg_linux.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/sockcmsg_linux.go	Tue Jan 29 10:54:22 2013 -0800
@@ -6,33 +6,31 @@ 
 
 package syscall
 
-import (
-	"unsafe"
-)
+import "unsafe"
 
 // UnixCredentials encodes credentials into a socket control message
 // for sending to another process. This can be used for
 // authentication.
 func UnixCredentials(ucred *Ucred) []byte {
-	buf := make([]byte, CmsgSpace(SizeofUcred))
-	cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
-	cmsg.Level = SOL_SOCKET
-	cmsg.Type = SCM_CREDENTIALS
-	cmsg.SetLen(CmsgLen(SizeofUcred))
-	*((*Ucred)(cmsgData(cmsg))) = *ucred
-	return buf
+	b := make([]byte, CmsgSpace(SizeofUcred))
+	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
+	h.Level = SOL_SOCKET
+	h.Type = SCM_CREDENTIALS
+	h.SetLen(CmsgLen(SizeofUcred))
+	*((*Ucred)(cmsgData(h))) = *ucred
+	return b
 }
 
 // ParseUnixCredentials decodes a socket control message that contains
 // credentials in a Ucred structure. To receive such a message, the
 // SO_PASSCRED option must be enabled on the socket.
-func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, error) {
-	if msg.Header.Level != SOL_SOCKET {
+func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
+	if m.Header.Level != SOL_SOCKET {
 		return nil, EINVAL
 	}
-	if msg.Header.Type != SCM_CREDENTIALS {
+	if m.Header.Type != SCM_CREDENTIALS {
 		return nil, EINVAL
 	}
-	ucred := *(*Ucred)(unsafe.Pointer(&msg.Data[0]))
+	ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
 	return &ucred, nil
 }
diff -r 03c4cdef61a5 libgo/go/syscall/sockcmsg_unix.go
--- a/libgo/go/syscall/sockcmsg_unix.go	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/go/syscall/sockcmsg_unix.go	Tue Jan 29 10:54:22 2013 -0800
@@ -8,9 +8,7 @@ 
 
 package syscall
 
-import (
-	"unsafe"
-)
+import "unsafe"
 
 // Round the length of a raw sockaddr up to align it propery.
 func cmsgAlignOf(salen int) int {
@@ -38,77 +36,69 @@ 
 	return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen)
 }
 
-func cmsgData(cmsg *Cmsghdr) unsafe.Pointer {
-	return unsafe.Pointer(uintptr(unsafe.Pointer(cmsg)) + SizeofCmsghdr)
+func cmsgData(h *Cmsghdr) unsafe.Pointer {
+	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)
 }
 
+// SocketControlMessage represents a socket control message.
 type SocketControlMessage struct {
 	Header Cmsghdr
 	Data   []byte
 }
 
-func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) {
-	var (
-		h     *Cmsghdr
-		dbuf  []byte
-		e     error
-		cmsgs []SocketControlMessage
-	)
-
-	for len(buf) >= CmsgLen(0) {
-		h, dbuf, e = socketControlMessageHeaderAndData(buf)
-		if e != nil {
-			break
+// ParseSocketControlMessage parses b as an array of socket control
+// messages.
+func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
+	var msgs []SocketControlMessage
+	for len(b) >= CmsgLen(0) {
+		h, dbuf, err := socketControlMessageHeaderAndData(b)
+		if err != nil {
+			return nil, err
 		}
-		m := SocketControlMessage{}
-		m.Header = *h
-		m.Data = dbuf[:int(h.Len)-cmsgAlignOf(SizeofCmsghdr)]
-		cmsgs = append(cmsgs, m)
-		buf = buf[cmsgAlignOf(int(h.Len)):]
+		m := SocketControlMessage{Header: *h, Data: dbuf[:int(h.Len)-cmsgAlignOf(SizeofCmsghdr)]}
+		msgs = append(msgs, m)
+		b = b[cmsgAlignOf(int(h.Len)):]
 	}
-
-	return cmsgs, e
+	return msgs, nil
 }
 
-func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) {
-	h := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
-	if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) {
+func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
+	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
+	if h.Len < SizeofCmsghdr || int(h.Len) > len(b) {
 		return nil, nil, EINVAL
 	}
-	return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil
+	return h, b[cmsgAlignOf(SizeofCmsghdr):], nil
 }
 
 // UnixRights encodes a set of open file descriptors into a socket
 // control message for sending to another process.
 func UnixRights(fds ...int) []byte {
 	datalen := len(fds) * 4
-	buf := make([]byte, CmsgSpace(datalen))
-	cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
-	cmsg.Level = SOL_SOCKET
-	cmsg.Type = SCM_RIGHTS
-	cmsg.SetLen(CmsgLen(datalen))
-
-	data := uintptr(cmsgData(cmsg))
+	b := make([]byte, CmsgSpace(datalen))
+	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
+	h.Level = SOL_SOCKET
+	h.Type = SCM_RIGHTS
+	h.SetLen(CmsgLen(datalen))
+	data := uintptr(cmsgData(h))
 	for _, fd := range fds {
 		*(*int32)(unsafe.Pointer(data)) = int32(fd)
 		data += 4
 	}
-
-	return buf
+	return b
 }
 
 // ParseUnixRights decodes a socket control message that contains an
 // integer array of open file descriptors from another process.
-func ParseUnixRights(msg *SocketControlMessage) ([]int, error) {
-	if msg.Header.Level != SOL_SOCKET {
+func ParseUnixRights(m *SocketControlMessage) ([]int, error) {
+	if m.Header.Level != SOL_SOCKET {
 		return nil, EINVAL
 	}
-	if msg.Header.Type != SCM_RIGHTS {
+	if m.Header.Type != SCM_RIGHTS {
 		return nil, EINVAL
 	}
-	fds := make([]int, len(msg.Data)>>2)
-	for i, j := 0, 0; i < len(msg.Data); i += 4 {
-		fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i])))
+	fds := make([]int, len(m.Data)>>2)
+	for i, j := 0, 0; i < len(m.Data); i += 4 {
+		fds[j] = int(*(*int32)(unsafe.Pointer(&m.Data[i])))
 		j++
 	}
 	return fds, nil
diff -r 03c4cdef61a5 libgo/mksysinfo.sh
--- a/libgo/mksysinfo.sh	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/mksysinfo.sh	Tue Jan 29 10:54:22 2013 -0800
@@ -303,6 +303,11 @@ 
     echo "const $m = 0" >> ${OUT}
   fi
 done
+for m in SOCK_CLOEXEC SOCK_NONBLOCK; do
+  if ! grep "^const $m " ${OUT} >/dev/null 2>&1; then
+    echo "const $m = -1" >> ${OUT}
+  fi
+done
 
 # pathconf constants.
 grep '^const __PC' gen-sysinfo.go |
diff -r 03c4cdef61a5 libgo/runtime/go-nosys.c
--- a/libgo/runtime/go-nosys.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/go-nosys.c	Tue Jan 29 10:54:22 2013 -0800
@@ -16,8 +16,10 @@ 
 #include <math.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <time.h>
 #include <unistd.h>
 
 #ifndef HAVE_OFF64_T
@@ -28,6 +30,19 @@ 
 typedef off64_t loff_t;
 #endif
 
+#ifndef HAVE_ACCEPT4
+struct sockaddr;
+int
+accept4 (int sockfd __attribute__ ((unused)),
+	 struct sockaddr *addr __attribute__ ((unused)),
+	 socklen_t *addrlen __attribute__ ((unused)),
+	 int flags __attribute__ ((unused)))
+{
+  errno = ENOSYS;
+  return -1;
+}
+#endif
+
 #ifndef HAVE_EPOLL_CREATE1
 int
 epoll_create1 (int flags __attribute__ ((unused)))
@@ -171,6 +186,16 @@ 
 }
 #endif
 
+#ifndef HAVE_PIPE2
+int
+pipe2 (int pipefd[2] __attribute__ ((unused)),
+       int flags __attribute__ ((unused)))
+{
+  errno = ENOSYS;
+  return -1;
+}
+#endif
+
 #ifndef HAVE_RENAMEAT
 int
 renameat (int olddirfd __attribute__ ((unused)),
@@ -241,6 +266,19 @@ 
 }
 #endif
 
+#ifndef HAVE_UTIMENSAT
+struct timespec;
+int
+utimensat(int dirfd __attribute__ ((unused)),
+	  const char *pathname __attribute__ ((unused)),
+	  const struct timespec times[2] __attribute__ ((unused)),
+	  int flags __attribute__ ((unused)))
+{
+  errno = ENOSYS;
+  return -1;
+}
+#endif
+
 /* Long double math functions.  These are needed on old i386 systems
    that don't have them in libm.  The compiler translates calls to
    these functions on float64 to call an 80-bit floating point
diff -r 03c4cdef61a5 libgo/runtime/go-setenv.c
--- a/libgo/runtime/go-setenv.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/go-setenv.c	Tue Jan 29 10:54:22 2013 -0800
@@ -25,20 +25,25 @@ 
   unsigned char *vn;
 
   ks = k.str;
+  if (ks == NULL)
+    ks = (const byte *) "";
   kn = NULL;
+
   vs = v.str;
+  if (vs == NULL)
+    vs = (const byte *) "";
   vn = NULL;
 
 #ifdef HAVE_SETENV
 
-  if (ks[k.len] != 0)
+  if (ks != NULL && ks[k.len] != 0)
     {
       kn = __go_alloc (k.len + 1);
       __builtin_memcpy (kn, ks, k.len);
       ks = kn;
     }
 
-  if (vs[v.len] != 0)
+  if (vs != NULL && vs[v.len] != 0)
     {
       vn = __go_alloc (v.len + 1);
       __builtin_memcpy (vn, vs, v.len);
diff -r 03c4cdef61a5 libgo/runtime/lock_futex.c
--- a/libgo/runtime/lock_futex.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/lock_futex.c	Tue Jan 29 10:54:22 2013 -0800
@@ -111,7 +111,8 @@ 
 void
 runtime_notewakeup(Note *n)
 {
-	runtime_xchg(&n->key, 1);
+	if(runtime_xchg(&n->key, 1))
+		runtime_throw("notewakeup - double wakeup");
 	runtime_futexwakeup(&n->key, 1);
 }
 
diff -r 03c4cdef61a5 libgo/runtime/malloc.goc
--- a/libgo/runtime/malloc.goc	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/malloc.goc	Tue Jan 29 10:54:22 2013 -0800
@@ -20,6 +20,8 @@ 
 
 MHeap runtime_mheap;
 
+int32	runtime_checking;
+
 extern MStats mstats;	// defined in zruntime_def_$GOOS_$GOARCH.go
 
 extern volatile intgo runtime_MemProfileRate
@@ -718,14 +720,22 @@ 
 
 	if(raceenabled)
 		runtime_m()->racepc = runtime_getcallerpc(&typ);
-	flag = typ->__code&GO_NO_POINTERS ? FlagNoPointers : 0;
-	ret = runtime_mallocgc(typ->__size, flag, 1, 1);
 
-	if(UseSpanType && !flag) {
-		if(false) {
-			runtime_printf("new %S: %p\n", *typ->__reflection, ret);
+	if(typ->__size == 0) {
+		// All 0-length allocations use this pointer.
+		// The language does not require the allocations to
+		// have distinct values.
+		ret = (uint8*)&runtime_zerobase;
+	} else {
+		flag = typ->__code&GO_NO_POINTERS ? FlagNoPointers : 0;
+		ret = runtime_mallocgc(typ->__size, flag, 1, 1);
+
+		if(UseSpanType && !flag) {
+			if(false) {
+				runtime_printf("new %S: %p\n", *typ->__reflection, ret);
+			}
+			runtime_settype(ret, (uintptr)typ | TypeInfo_SingleObject);
 		}
-		runtime_settype(ret, (uintptr)typ | TypeInfo_SingleObject);
 	}
 
 	return ret;
diff -r 03c4cdef61a5 libgo/runtime/malloc.h
--- a/libgo/runtime/malloc.h	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/malloc.h	Tue Jan 29 10:54:22 2013 -0800
@@ -446,7 +446,7 @@ 
 void	runtime_checkallocated(void *v, uintptr n);
 void	runtime_markfreed(void *v, uintptr n);
 void	runtime_checkfreed(void *v, uintptr n);
-int32	runtime_checking;
+extern	int32	runtime_checking;
 void	runtime_markspan(void *v, uintptr size, uintptr n, bool leftover);
 void	runtime_unmarkspan(void *v, uintptr size);
 bool	runtime_blockspecial(void*);
@@ -500,6 +500,7 @@ 
 
 // defined in mgc0.go
 void	runtime_gc_m_ptr(Eface*);
+void	runtime_gc_itab_ptr(Eface*);
 
 void	runtime_memorydump(void);
 
diff -r 03c4cdef61a5 libgo/runtime/mcentral.c
--- a/libgo/runtime/mcentral.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/mcentral.c	Tue Jan 29 10:54:22 2013 -0800
@@ -19,7 +19,6 @@ 
 #include "malloc.h"
 
 static bool MCentral_Grow(MCentral *c);
-static void* MCentral_Alloc(MCentral *c);
 static void MCentral_Free(MCentral *c, void *v);
 
 // Initialize a single central free list.
@@ -34,12 +33,13 @@ 
 // Allocate up to n objects from the central free list.
 // Return the number of objects allocated.
 // The objects are linked together by their first words.
-// On return, *pstart points at the first object and *pend at the last.
+// On return, *pstart points at the first object.
 int32
 runtime_MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst)
 {
-	MLink *first, *last, *v;
-	int32 i;
+	MSpan *s;
+	MLink *first, *last;
+	int32 cap, avail, i;
 
 	runtime_lock(c);
 	// Replenish central list if empty.
@@ -50,41 +50,34 @@ 
 			return 0;
 		}
 	}
+	s = c->nonempty.next;
+	cap = (s->npages << PageShift) / s->elemsize;
+	avail = cap - s->ref;
+	if(avail < n)
+		n = avail;
 
-	// Copy from list, up to n.
 	// First one is guaranteed to work, because we just grew the list.
-	first = MCentral_Alloc(c);
+	first = s->freelist;
 	last = first;
-	for(i=1; i<n && (v = MCentral_Alloc(c)) != nil; i++) {
-		last->next = v;
-		last = v;
+	for(i=1; i<n; i++) {
+		last = last->next;
 	}
+	s->freelist = last->next;
 	last->next = nil;
-	c->nfree -= i;
+	s->ref += n;
+	c->nfree -= n;
+
+	if(n == avail) {
+		if(s->freelist != nil || s->ref != (uint32)cap) {
+			runtime_throw("invalid freelist");
+		}
+		runtime_MSpanList_Remove(s);
+		runtime_MSpanList_Insert(&c->empty, s);
+	}
 
 	runtime_unlock(c);
 	*pfirst = first;
-	return i;
-}
-
-// Helper: allocate one object from the central free list.
-static void*
-MCentral_Alloc(MCentral *c)
-{
-	MSpan *s;
-	MLink *v;
-
-	if(runtime_MSpanList_IsEmpty(&c->nonempty))
-		return nil;
-	s = c->nonempty.next;
-	s->ref++;
-	v = s->freelist;
-	s->freelist = v->next;
-	if(s->freelist == nil) {
-		runtime_MSpanList_Remove(s);
-		runtime_MSpanList_Insert(&c->empty, s);
-	}
-	return v;
+	return n;
 }
 
 // Free n objects back into the central free list.
diff -r 03c4cdef61a5 libgo/runtime/mem.c
--- a/libgo/runtime/mem.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/mem.c	Tue Jan 29 10:54:22 2013 -0800
@@ -81,6 +81,10 @@ 
 			runtime_printf("if you're running SELinux, enable execmem for this process.\n");
 			exit(2);
 		}
+		if(errno == EAGAIN) {
+			runtime_printf("runtime: mmap: too much locked memory (check 'ulimit -l').\n");
+			runtime_exit(2);
+		}
 		return nil;
 	}
 	return p;
diff -r 03c4cdef61a5 libgo/runtime/mgc0.c
--- a/libgo/runtime/mgc0.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/mgc0.c	Tue Jan 29 10:54:22 2013 -0800
@@ -11,6 +11,22 @@ 
 #include "malloc.h"
 #include "mgc0.h"
 #include "race.h"
+#include "go-type.h"
+
+// Map gccgo field names to gc field names.
+// Slice aka __go_open_array.
+#define array __values
+#define cap __capacity
+// Iface aka __go_interface
+#define tab __methods
+// Eface aka __go_empty_interface.
+#define type __type_descriptor
+// Type aka __go_type_descriptor
+#define kind __code
+#define KindPtr GO_PTR
+#define KindNoPointers GO_NO_POINTERS
+// PtrType aka __go_ptr_type
+#define elem __element_type
 
 #ifdef USING_SPLIT_STACK
 
@@ -32,6 +48,11 @@ 
 
 	handoffThreshold = 4,
 	IntermediateBufferCapacity = 64,
+
+	// Bits in type information
+	PRECISE = 1,
+	LOOP = 2,
+	PC_BITS = PRECISE | LOOP,
 };
 
 // Bits in per-word bitmap.
@@ -158,12 +179,14 @@ 
 // is moved/flushed to the work buffer (Workbuf).
 // The size of an intermediate buffer is very small,
 // such as 32 or 64 elements.
+typedef struct PtrTarget PtrTarget;
 struct PtrTarget
 {
 	void *p;
 	uintptr ti;
 };
 
+typedef struct BitTarget BitTarget;
 struct BitTarget
 {
 	void *p;
@@ -171,15 +194,19 @@ 
 	uintptr *bitp, shift;
 };
 
+typedef struct BufferList BufferList;
 struct BufferList
 {
-	struct PtrTarget ptrtarget[IntermediateBufferCapacity];
-	struct BitTarget bittarget[IntermediateBufferCapacity];
-	struct BufferList *next;
+	PtrTarget ptrtarget[IntermediateBufferCapacity];
+	BitTarget bittarget[IntermediateBufferCapacity];
+	BufferList *next;
 };
-static struct BufferList *bufferList;
+static BufferList *bufferList;
 
 static Lock lock;
+static Type *itabtype;
+
+static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj);
 
 // flushptrbuf moves data from the PtrTarget buffer to the work buffer.
 // The PtrTarget buffer contains blocks irrespective of whether the blocks have been marked or scanned,
@@ -202,16 +229,16 @@ 
 //  flushptrbuf
 //  (2nd part, mark and enqueue)
 static void
-flushptrbuf(struct PtrTarget *ptrbuf, uintptr n, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, struct BitTarget *bitbuf)
+flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, BitTarget *bitbuf)
 {
 	byte *p, *arena_start, *obj;
-	uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti;
+	uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti, n;
 	MSpan *s;
 	PageID k;
 	Obj *wp;
 	Workbuf *wbuf;
-	struct PtrTarget *ptrbuf_end;
-	struct BitTarget *bitbufpos, *bt;
+	PtrTarget *ptrbuf_end;
+	BitTarget *bitbufpos, *bt;
 
 	arena_start = runtime_mheap.arena_start;
 
@@ -219,7 +246,9 @@ 
 	wbuf = *_wbuf;
 	nobj = *_nobj;
 
-	ptrbuf_end = ptrbuf + n;
+	ptrbuf_end = *ptrbufpos;
+	n = ptrbuf_end - ptrbuf;
+	*ptrbufpos = ptrbuf;
 
 	// If buffer is nearly full, get a new one.
 	if(wbuf == nil || nobj+n >= nelem(wbuf->obj)) {
@@ -318,8 +347,7 @@ 
 			if((bits & (bitAllocated|bitMarked)) != bitAllocated)
 				continue;
 
-			*bitbufpos = (struct BitTarget){obj, ti, bitp, shift};
-			bitbufpos++;
+			*bitbufpos++ = (BitTarget){obj, ti, bitp, shift};
 		}
 
 		runtime_lock(&lock);
@@ -370,6 +398,13 @@ 
 // Program that scans the whole block and treats every block element as a potential pointer
 static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR};
 
+// Local variables of a program fragment or loop
+typedef struct Frame Frame;
+struct Frame {
+	uintptr count, elemsize, b;
+	uintptr *loop_or_ret;
+};
+
 // scanblock scans a block of n bytes starting at pointer b for references
 // to other objects, scanning any it finds recursively until there are no
 // unscanned objects left.  Instead of using an explicit recursion, it keeps
@@ -384,22 +419,17 @@ 
 scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
 {
 	byte *b, *arena_start, *arena_used;
-	uintptr n, i, end_b;
+	uintptr n, i, end_b, elemsize, ti, objti, count /* , type */;
+	uintptr *pc, precise_type, nominal_size;
 	void *obj;
-
-	// TODO(atom): to be expanded in a next CL
-	struct Frame {uintptr count, b; uintptr *loop_or_ret;};
-	struct Frame stack_top;
-
-	uintptr *pc;
-
-	struct BufferList *scanbuffers;
-	struct PtrTarget *ptrbuf, *ptrbuf_end;
-	struct BitTarget *bitbuf;
-
-	struct PtrTarget *ptrbufpos;
-
-	// End of local variable declarations.
+	const Type *t;
+	Slice *sliceptr;
+	Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4];
+	BufferList *scanbuffers;
+	PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos;
+	BitTarget *bitbuf;
+	Eface *eface;
+	Iface *iface;
 
 	if(sizeof(Workbuf) % PageSize != 0)
 		runtime_throw("scanblock: size of Workbuf is suboptimal");
@@ -408,6 +438,11 @@ 
 	arena_start = runtime_mheap.arena_start;
 	arena_used = runtime_mheap.arena_used;
 
+	stack_ptr = stack+nelem(stack)-1;
+	
+	precise_type = false;
+	nominal_size = 0;
+
 	// Allocate ptrbuf, bitbuf
 	{
 		runtime_lock(&lock);
@@ -437,50 +472,247 @@ 
 			runtime_printf("scanblock %p %D\n", b, (int64)n);
 		}
 
-		// TODO(atom): to be replaced in a next CL
-		pc = defaultProg;
+		if(ti != 0 && 0) {
+			pc = (uintptr*)(ti & ~(uintptr)PC_BITS);
+			precise_type = (ti & PRECISE);
+			stack_top.elemsize = pc[0];
+			if(!precise_type)
+				nominal_size = pc[0];
+			if(ti & LOOP) {
+				stack_top.count = 0;	// 0 means an infinite number of iterations
+				stack_top.loop_or_ret = pc+1;
+			} else {
+				stack_top.count = 1;
+			}
+		} else if(UseSpanType && 0) {
+#if 0
+			type = runtime_gettype(b);
+			if(type != 0) {
+				t = (Type*)(type & ~(uintptr)(PtrSize-1));
+				switch(type & (PtrSize-1)) {
+				case TypeInfo_SingleObject:
+					pc = (uintptr*)t->gc;
+					precise_type = true;  // type information about 'b' is precise
+					stack_top.count = 1;
+					stack_top.elemsize = pc[0];
+					break;
+				case TypeInfo_Array:
+					pc = (uintptr*)t->gc;
+					if(pc[0] == 0)
+						goto next_block;
+					precise_type = true;  // type information about 'b' is precise
+					stack_top.count = 0;  // 0 means an infinite number of iterations
+					stack_top.elemsize = pc[0];
+					stack_top.loop_or_ret = pc+1;
+					break;
+				case TypeInfo_Map:
+					// TODO(atom): to be expanded in a next CL
+					pc = defaultProg;
+					break;
+				default:
+					runtime_throw("scanblock: invalid type");
+					return;
+				}
+			} else {
+				pc = defaultProg;
+			}
+#endif
+		} else {
+			pc = defaultProg;
+		}
 
 		pc++;
 		stack_top.b = (uintptr)b;
 
 		end_b = (uintptr)b + n - PtrSize;
 
-	next_instr:
-		// TODO(atom): to be expanded in a next CL
+	for(;;) {
+		obj = nil;
+		objti = 0;
 		switch(pc[0]) {
+		case GC_PTR:
+			obj = *(void**)(stack_top.b + pc[1]);
+			objti = pc[2];
+			pc += 3;
+			break;
+
+		case GC_SLICE:
+			sliceptr = (Slice*)(stack_top.b + pc[1]);
+			if(sliceptr->cap != 0) {
+				obj = sliceptr->array;
+				objti = pc[2] | PRECISE | LOOP;
+			}
+			pc += 3;
+			break;
+
+		case GC_APTR:
+			obj = *(void**)(stack_top.b + pc[1]);
+			pc += 2;
+			break;
+
+		case GC_STRING:
+			obj = *(void**)(stack_top.b + pc[1]);
+			pc += 2;
+			break;
+
+		case GC_EFACE:
+			eface = (Eface*)(stack_top.b + pc[1]);
+			pc += 2;
+			if(eface->type != nil && ((byte*)eface->__object >= arena_start && (byte*)eface->__object < arena_used)) {
+				t = eface->type;
+				if(t->__size <= sizeof(void*)) {
+					if((t->kind & KindNoPointers))
+						break;
+
+					obj = eface->__object;
+					if((t->kind & ~KindNoPointers) == KindPtr)
+						// objti = (uintptr)((PtrType*)t)->elem->gc;
+						objti = 0;
+				} else {
+					obj = eface->__object;
+					// objti = (uintptr)t->gc;
+					objti = 0;
+				}
+			}
+			break;
+
+		case GC_IFACE:
+			iface = (Iface*)(stack_top.b + pc[1]);
+			pc += 2;
+			if(iface->tab == nil)
+				break;
+			
+			// iface->tab
+			if((byte*)iface->tab >= arena_start && (byte*)iface->tab < arena_used) {
+				// *ptrbufpos++ = (struct PtrTarget){iface->tab, (uintptr)itabtype->gc};
+				*ptrbufpos++ = (struct PtrTarget){iface->tab, 0};
+				if(ptrbufpos == ptrbuf_end)
+					flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+			}
+
+			// iface->data
+			if((byte*)iface->__object >= arena_start && (byte*)iface->__object < arena_used) {
+				// t = iface->tab->type;
+				t = nil;
+				if(t->__size <= sizeof(void*)) {
+					if((t->kind & KindNoPointers))
+						break;
+
+					obj = iface->__object;
+					if((t->kind & ~KindNoPointers) == KindPtr)
+						// objti = (uintptr)((const PtrType*)t)->elem->gc;
+						objti = 0;
+				} else {
+					obj = iface->__object;
+					// objti = (uintptr)t->gc;
+					objti = 0;
+				}
+			}
+			break;
+
 		case GC_DEFAULT_PTR:
-			while(true) {
-				i = stack_top.b;
-				if(i > end_b)
-					goto next_block;
+			while((i = stack_top.b) <= end_b) {
 				stack_top.b += PtrSize;
-
 				obj = *(byte**)i;
 				if((byte*)obj >= arena_start && (byte*)obj < arena_used) {
-					*ptrbufpos = (struct PtrTarget){obj, 0};
-					ptrbufpos++;
+					*ptrbufpos++ = (struct PtrTarget){obj, 0};
 					if(ptrbufpos == ptrbuf_end)
-						goto flush_buffers;
+						flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
 				}
 			}
+			goto next_block;
+
+		case GC_END:
+			if(--stack_top.count != 0) {
+				// Next iteration of a loop if possible.
+				elemsize = stack_top.elemsize;
+				stack_top.b += elemsize;
+				if(stack_top.b + elemsize <= end_b+PtrSize) {
+					pc = stack_top.loop_or_ret;
+					continue;
+				}
+				i = stack_top.b;
+			} else {
+				// Stack pop if possible.
+				if(stack_ptr+1 < stack+nelem(stack)) {
+					pc = stack_top.loop_or_ret;
+					stack_top = *(++stack_ptr);
+					continue;
+				}
+				i = (uintptr)b + nominal_size;
+			}
+			if(!precise_type) {
+				// Quickly scan [b+i,b+n) for possible pointers.
+				for(; i<=end_b; i+=PtrSize) {
+					if(*(byte**)i != nil) {
+						// Found a value that may be a pointer.
+						// Do a rescan of the entire block.
+						enqueue((Obj){b, n, 0}, &wbuf, &wp, &nobj);
+						break;
+					}
+				}
+			}
+			goto next_block;
+
+		case GC_ARRAY_START:
+			i = stack_top.b + pc[1];
+			count = pc[2];
+			elemsize = pc[3];
+			pc += 4;
+
+			// Stack push.
+			*stack_ptr-- = stack_top;
+			stack_top = (Frame){count, elemsize, i, pc};
+			continue;
+
+		case GC_ARRAY_NEXT:
+			if(--stack_top.count != 0) {
+				stack_top.b += stack_top.elemsize;
+				pc = stack_top.loop_or_ret;
+			} else {
+				// Stack pop.
+				stack_top = *(++stack_ptr);
+				pc += 1;
+			}
+			continue;
+
+		case GC_CALL:
+			// Stack push.
+			*stack_ptr-- = stack_top;
+			stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/};
+			pc = (uintptr*)pc[2];  // target of the CALL instruction
+			continue;
+
+		case GC_MAP_PTR:
+			// TODO(atom): to be expanded in a next CL. Same as GC_APTR for now.
+			obj = *(void**)(stack_top.b + pc[1]);
+			pc += 3;
+			break;
+
+		case GC_REGION:
+			// TODO(atom): to be expanded in a next CL. Same as GC_APTR for now.
+			obj = (void*)(stack_top.b + pc[1]);
+			pc += 4;
+			break;
 
 		default:
 			runtime_throw("scanblock: invalid GC instruction");
 			return;
 		}
 
-	flush_buffers:
-		flushptrbuf(ptrbuf, ptrbufpos-ptrbuf, &wp, &wbuf, &nobj, bitbuf);
-		ptrbufpos = ptrbuf;
-		goto next_instr;
+		if((byte*)obj >= arena_start && (byte*)obj < arena_used) {
+			*ptrbufpos++ = (PtrTarget){obj, objti};
+			if(ptrbufpos == ptrbuf_end)
+				flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+		}
+	}
 
 	next_block:
 		// Done scanning [b, b+n).  Prepare for the next iteration of
-		// the loop by setting b, n to the parameters for the next block.
+		// the loop by setting b, n, ti to the parameters for the next block.
 
 		if(nobj == 0) {
-			flushptrbuf(ptrbuf, ptrbufpos-ptrbuf, &wp, &wbuf, &nobj, bitbuf);
-			ptrbufpos = ptrbuf;
+			flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
 
 			if(nobj == 0) {
 				if(!keepworking) {
@@ -501,6 +733,7 @@ 
 		--wp;
 		b = wp->p;
 		n = wp->n;
+		ti = wp->ti;
 		nobj--;
 	}
 
@@ -1004,10 +1237,6 @@ 
 
 	USED(&desc);
 	s = runtime_mheap.allspans[idx];
-	// Stamp newly unused spans. The scavenger will use that
-	// info to potentially give back some pages to the OS.
-	if(s->state == MSpanFree && s->unusedsince == 0)
-		s->unusedsince = runtime_nanotime();
 	if(s->state != MSpanInUse)
 		return;
 	arena_start = runtime_mheap.arena_start;
@@ -1229,18 +1458,15 @@ 
 	MCache *c;
 	uint32 i;
 	uint64 stacks_inuse;
-	uint64 stacks_sys;
 	uint64 *src, *dst;
 
 	if(stats)
 		runtime_memclr((byte*)stats, sizeof(*stats));
 	stacks_inuse = 0;
-	stacks_sys = runtime_stacks_sys;
 	for(mp=runtime_allm; mp; mp=mp->alllink) {
 		c = mp->mcache;
 		runtime_purgecachedstats(c);
-		// stacks_inuse += mp->stackalloc->inuse;
-		// stacks_sys += mp->stackalloc->sys;
+		// stacks_inuse += mp->stackinuse*FixedStack;
 		if(stats) {
 			src = (uint64*)&mp->gcstats;
 			dst = (uint64*)stats;
@@ -1256,7 +1482,6 @@ 
 		}
 	}
 	mstats.stacks_inuse = stacks_inuse;
-	mstats.stacks_sys = stacks_sys;
 }
 
 // Structure of arguments passed to function gc().
@@ -1330,11 +1555,12 @@ 
 gc(struct gc_args *args)
 {
 	M *m;
-	int64 t0, t1, t2, t3;
+	int64 t0, t1, t2, t3, t4;
 	uint64 heap0, heap1, obj0, obj1;
 	GCStats stats;
 	M *mp;
 	uint32 i;
+	// Eface eface;
 
 	runtime_semacquire(&runtime_worldsema);
 	if(!args->force && mstats.heap_alloc < mstats.next_gc) {
@@ -1367,6 +1593,12 @@ 
 		work.sweepfor = runtime_parforalloc(MaxGcproc);
 	m->locks--;
 
+	if(itabtype == nil) {
+		// get C pointer to the Go type "itab"
+		// runtime_gc_itab_ptr(&eface);
+		// itabtype = ((PtrType*)eface.type)->elem;
+	}
+
 	work.nwait = 0;
 	work.ndone = 0;
 	work.debugmarkdone = 0;
@@ -1379,6 +1611,8 @@ 
 		runtime_helpgc(work.nproc);
 	}
 
+	t1 = runtime_nanotime();
+
 	runtime_parfordo(work.markfor);
 	scanblock(nil, nil, 0, true);
 
@@ -1387,10 +1621,10 @@ 
 			debug_scanblock(work.roots[i].p, work.roots[i].n);
 		runtime_atomicstore(&work.debugmarkdone, 1);
 	}
-	t1 = runtime_nanotime();
+	t2 = runtime_nanotime();
 
 	runtime_parfordo(work.sweepfor);
-	t2 = runtime_nanotime();
+	t3 = runtime_nanotime();
 
 	stealcache();
 	cachestats(&stats);
@@ -1420,18 +1654,18 @@ 
 	heap1 = mstats.heap_alloc;
 	obj1 = mstats.nmalloc - mstats.nfree;
 
-	t3 = runtime_nanotime();
-	mstats.last_gc = t3;
-	mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0;
-	mstats.pause_total_ns += t3 - t0;
+	t4 = runtime_nanotime();
+	mstats.last_gc = t4;
+	mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
+	mstats.pause_total_ns += t4 - t0;
 	mstats.numgc++;
 	if(mstats.debuggc)
-		runtime_printf("pause %D\n", t3-t0);
+		runtime_printf("pause %D\n", t4-t0);
 
 	if(gctrace) {
 		runtime_printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB %D -> %D (%D-%D) objects,"
 				" %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n",
-			mstats.numgc, work.nproc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000,
+			mstats.numgc, work.nproc, (t2-t1)/1000000, (t3-t2)/1000000, (t1-t0+t4-t3)/1000000,
 			heap0>>20, heap1>>20, obj0, obj1,
 			mstats.nmalloc, mstats.nfree,
 			stats.nhandoff, stats.nhandoffcnt,
diff -r 03c4cdef61a5 libgo/runtime/mheap.c
--- a/libgo/runtime/mheap.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/mheap.c	Tue Jan 29 10:54:22 2013 -0800
@@ -138,7 +138,9 @@ 
 		*(uintptr*)(t->start<<PageShift) = *(uintptr*)(s->start<<PageShift);  // copy "needs zeroing" mark
 		t->state = MSpanInUse;
 		MHeap_FreeLocked(h, t);
+		t->unusedsince = s->unusedsince; // preserve age
 	}
+	s->unusedsince = 0;
 
 	// Record span info, because gc needs to be
 	// able to map interior pointer to containing span.
@@ -300,10 +302,12 @@ 
 	}
 	mstats.heap_idle += s->npages<<PageShift;
 	s->state = MSpanFree;
-	s->unusedsince = 0;
-	s->npreleased = 0;
 	runtime_MSpanList_Remove(s);
 	sp = (uintptr*)(s->start<<PageShift);
+	// Stamp newly unused spans. The scavenger will use that
+	// info to potentially give back some pages to the OS.
+	s->unusedsince = runtime_nanotime();
+	s->npreleased = 0;
 
 	// Coalesce with earlier, later spans.
 	p = s->start;
@@ -405,10 +409,10 @@ 
 			runtime_entersyscall();
 			runtime_notesleep(&note);
 			runtime_exitsyscall();
+			if(trace)
+				runtime_printf("scvg%d: GC forced\n", k);
 			runtime_lock(h);
 			now = runtime_nanotime();
-			if (trace)
-				runtime_printf("scvg%d: GC forced\n", k);
 		}
 		sumreleased = 0;
 		for(i=0; i < nelem(h->free)+1; i++) {
@@ -419,7 +423,7 @@ 
 			if(runtime_MSpanList_IsEmpty(list))
 				continue;
 			for(s=list->next; s != list; s=s->next) {
-				if(s->unusedsince != 0 && (now - s->unusedsince) > limit) {
+				if((now - s->unusedsince) > limit) {
 					released = (s->npages - s->npreleased) << PageShift;
 					mstats.heap_released += released;
 					sumreleased += released;
diff -r 03c4cdef61a5 libgo/runtime/panic.c
--- a/libgo/runtime/panic.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/panic.c	Tue Jan 29 10:54:22 2013 -0800
@@ -87,7 +87,7 @@ 
 runtime_throw(const char *s)
 {
 	runtime_startpanic();
-	runtime_printf("throw: %s\n", s);
+	runtime_printf("fatal error: %s\n", s);
 	runtime_dopanic(0);
 	*(int32*)0 = 0;	// not reached
 	runtime_exit(1);	// even more not reached
diff -r 03c4cdef61a5 libgo/runtime/proc.c
--- a/libgo/runtime/proc.c	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/proc.c	Tue Jan 29 10:54:22 2013 -0800
@@ -167,6 +167,13 @@ 
 
 int32	runtime_gcwaiting;
 
+G*	runtime_allg;
+G*	runtime_lastg;
+M*	runtime_allm;
+
+int8*	runtime_goos;
+int32	runtime_ncpu;
+
 // The static TLS size.  See runtime_newm.
 static int tlssize;
 
diff -r 03c4cdef61a5 libgo/runtime/runtime.h
--- a/libgo/runtime/runtime.h	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/runtime.h	Tue Jan 29 10:54:22 2013 -0800
@@ -119,6 +119,13 @@ 
 {
 	PtrSize = sizeof(void*),
 };
+enum
+{
+	// Per-M stack segment cache size.
+	StackCacheSize = 32,
+	// Global <-> per-M stack segment cache transfer batch size.
+	StackCacheBatch = 16,
+};
 
 /*
  * structures
@@ -178,6 +185,8 @@ 
 	int32	sig;
 	int32	writenbuf;
 	byte*	writebuf;
+	// DeferChunk	*dchunk;
+	// DeferChunk	*dchunknext;
 	uintptr	sigcode0;
 	uintptr	sigcode1;
 	// uintptr	sigpc;
@@ -344,14 +353,14 @@ 
  * external data
  */
 extern	uintptr runtime_zerobase;
-G*	runtime_allg;
-G*	runtime_lastg;
-M*	runtime_allm;
+extern	G*	runtime_allg;
+extern	G*	runtime_lastg;
+extern	M*	runtime_allm;
 extern	int32	runtime_gomaxprocs;
 extern	bool	runtime_singleproc;
 extern	uint32	runtime_panicking;
 extern	int32	runtime_gcwaiting;		// gc is waiting to run
-int32	runtime_ncpu;
+extern	int32	runtime_ncpu;
 
 /*
  * common functions and data
diff -r 03c4cdef61a5 libgo/runtime/sigqueue.goc
--- a/libgo/runtime/sigqueue.goc	Tue Jan 29 05:57:54 2013 -0800
+++ b/libgo/runtime/sigqueue.goc	Tue Jan 29 10:54:22 2013 -0800
@@ -5,36 +5,24 @@ 
 // This file implements runtime support for signal handling.
 //
 // Most synchronization primitives are not available from
-// the signal handler (it cannot block and cannot use locks)
+// the signal handler (it cannot block, allocate memory, or use locks)
 // so the handler communicates with a processing goroutine
 // via struct sig, below.
 //
-// Ownership for sig.Note passes back and forth between
-// the signal handler and the signal goroutine in rounds.
-// The initial state is that sig.note is cleared (setup by signal_enable).
-// At the beginning of each round, mask == 0.
-// The round goes through three stages:
-//
-// (In parallel)
-// 1a) One or more signals arrive and are handled
-// by sigsend using cas to set bits in sig.mask.
-// The handler that changes sig.mask from zero to non-zero
-// calls notewakeup(&sig).
-// 1b) Sigrecv calls notesleep(&sig) to wait for the wakeup.
-//
-// 2) Having received the wakeup, sigrecv knows that sigsend
-// will not send another wakeup, so it can noteclear(&sig)
-// to prepare for the next round. (Sigsend may still be adding
-// signals to sig.mask at this point, which is fine.)
-//
-// 3) Sigrecv uses cas to grab the current sig.mask and zero it,
-// triggering the next round.
-//
-// The signal handler takes ownership of the note by atomically
-// changing mask from a zero to non-zero value. It gives up
-// ownership by calling notewakeup. The signal goroutine takes
-// ownership by returning from notesleep (caused by the notewakeup)
-// and gives up ownership by clearing mask.
+// sigsend() is called by the signal handler to queue a new signal.
+// signal_recv() is called by the Go program to receive a newly queued signal.
+// Synchronization between sigsend() and signal_recv() is based on the sig.state
+// variable.  It can be in 3 states: 0, HASWAITER and HASSIGNAL.
+// HASWAITER means that signal_recv() is blocked on sig.Note and there are no
+// new pending signals.
+// HASSIGNAL means that sig.mask *may* contain new pending signals,
+// signal_recv() can't be blocked in this state.
+// 0 means that there are no new pending signals and signal_recv() is not blocked.
+// Transitions between states are done atomically with CAS.
+// When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask.
+// If several sigsend()'s and signal_recv() execute concurrently, it can lead to
+// unnecessary rechecks of sig.mask, but must not lead to missed signals
+// nor deadlocks.
 
 package signal
 #include "config.h"
@@ -47,15 +35,20 @@ 
 	Note;
 	uint32 mask[(NSIG+31)/32];
 	uint32 wanted[(NSIG+31)/32];
-	uint32 kick;
+	uint32 state;
 	bool inuse;
 } sig;
 
+enum {
+	HASWAITER = 1,
+	HASSIGNAL = 2,
+};
+
 // Called from sighandler to send a signal back out of the signal handling thread.
 bool
 __go_sigsend(int32 s)
 {
-	uint32 bit, mask;
+	uint32 bit, mask, old, new;
 
 	if(!sig.inuse || s < 0 || (size_t)s >= 32*nelem(sig.wanted) || !(sig.wanted[s/32]&(1U<<(s&31))))
 		return false;
@@ -67,8 +60,20 @@ 
 		if(runtime_cas(&sig.mask[s/32], mask, mask|bit)) {
 			// Added to queue.
 			// Only send a wakeup if the receiver needs a kick.
-			if(runtime_cas(&sig.kick, 1, 0))
-				runtime_notewakeup(&sig);
+			for(;;) {
+				old = runtime_atomicload(&sig.state);
+				if(old == HASSIGNAL)
+					break;
+				if(old == HASWAITER)
+					new = 0;
+				else  // if(old == 0)
+					new = HASSIGNAL;
+				if(runtime_cas(&sig.state, old, new)) {
+					if (old == HASWAITER)
+						runtime_notewakeup(&sig);
+					break;
+				}
+			}
 			break;
 		}
 	}
@@ -79,7 +84,7 @@ 
 // Must only be called from a single goroutine at a time.
 func signal_recv() (m uint32) {
 	static uint32 recv[nelem(sig.mask)];
-	int32 i, more;
+	uint32 i, old, new;
 	
 	for(;;) {
 		// Serve from local copy if there are bits left.
@@ -91,15 +96,27 @@ 
 			}
 		}
 
-		// Get a new local copy.
-		// Ask for a kick if more signals come in
-		// during or after our check (before the sleep).
-		if(sig.kick == 0) {
-			runtime_noteclear(&sig);
-			runtime_cas(&sig.kick, 0, 1);
+		// Check and update sig.state.
+		for(;;) {
+			old = runtime_atomicload(&sig.state);
+			if(old == HASWAITER)
+				runtime_throw("inconsistent state in signal_recv");
+			if(old == HASSIGNAL)
+				new = 0;
+			else  // if(old == 0)
+				new = HASWAITER;
+			if(runtime_cas(&sig.state, old, new)) {
+				if (new == HASWAITER) {
+					runtime_entersyscall();
+					runtime_notesleep(&sig);
+					runtime_exitsyscall();
+					runtime_noteclear(&sig);
+				}
+				break;
+			}
 		}
 
-		more = 0;
+		// Get a new local copy.
 		for(i=0; (size_t)i<nelem(sig.mask); i++) {
 			for(;;) {
 				m = sig.mask[i];
@@ -107,16 +124,7 @@ 
 					break;
 			}
 			recv[i] = m;
-			if(m != 0)
-				more = 1;
 		}
-		if(more)
-			continue;
-
-		// Sleep waiting for more.
-		runtime_entersyscall();
-		runtime_notesleep(&sig);
-		runtime_exitsyscall();
 	}
 
 done:;