delete unused code and data from 6.outs.
cuts simple test binary by 7%.
would be more except for reflection.

R=r
DELTA=126  (117 added, 4 deleted, 5 changed)
OCL=23163
CL=23237
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 68e7ec5..f1972f8 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -708,8 +708,10 @@
 				if(p->to.sym) {
 					if(p->to.sym->type == SUNDEF)
 						ckoff(p->to.sym, o);
-					if(p->to.sym->type == Sxxx)
+					if(p->to.sym->type == Sxxx) {
+						curtext = p;	// show useful name in diag's output
 						diag("missing symbol %s", p->to.sym->name);
+					}
 					o += p->to.sym->value;
 					if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF)
 						o += INITDAT;
diff --git a/src/cmd/6l/go.c b/src/cmd/6l/go.c
index 07a0f21..a1a515f 100644
--- a/src/cmd/6l/go.c
+++ b/src/cmd/6l/go.c
@@ -514,10 +514,107 @@
 		if(p->to.sym != S && p->to.sym->type == SOPT) {
 			if(p->as != ACALL)
 				diag("bad use of optional function: %P", p);
-			p->as = ANOP;
-			p->from.type = D_NONE;
-			p->to.type = D_NONE;
+			nopout(p);
 		}
 	}
 }
 
+static void mark(Sym*);
+static int markdepth;
+
+static void
+markdata(Prog *p, Sym *s)
+{
+	markdepth++;
+	if(p != P && debug['v'] > 1)
+		Bprint(&bso, "%d markdata %s\n", markdepth, s->name);
+	for(; p != P; p=p->dlink)
+		if(p->to.sym)
+			mark(p->to.sym);
+	markdepth--;
+}
+
+static void
+marktext(Prog *p)
+{
+	if(p == P)
+		return;
+	if(p->as != ATEXT) {
+		diag("marktext: %P", p);
+		return;
+	}
+	markdepth++;
+	if(debug['v'] > 1)
+		Bprint(&bso, "%d marktext %s\n", markdepth, p->from.sym->name);
+	for(p=p->link; p != P; p=p->link) {
+		if(p->as == ATEXT || p->as == ADATA || p->as == AGLOBL)
+			break;
+		if(p->from.sym)
+			mark(p->from.sym);
+		if(p->to.sym)
+			mark(p->to.sym);
+	}
+	markdepth--;
+}
+
+static void
+mark(Sym *s)
+{
+	if(s == S || s->reachable)
+		return;
+	s->reachable = 1;
+	if(s->text)
+		marktext(s->text);
+	if(s->data)
+		markdata(s->data, s);
+}
+
+static void
+sweeplist(Prog **first, Prog **last)
+{
+	int reachable;
+	Prog *p, *q;
+
+	reachable = 1;
+	q = P;
+	for(p=*first; p != P; p=p->link) {
+		switch(p->as) {
+		case ATEXT:
+		case ADATA:
+		case AGLOBL:
+			reachable = p->from.sym->reachable;
+			if(!reachable) {
+				if(debug['v'] > 1)
+					Bprint(&bso, "discard %s\n", p->from.sym->name);
+				p->from.sym->type = Sxxx;
+			}
+			break;
+		}
+		if(reachable) {
+			if(q == P)
+				*first = p;
+			else
+				q->link = p;
+			q = p;
+		}
+	}
+	if(q == P)
+		*first = P;
+	else
+		q->link = P;
+	*last = q;
+}
+
+void
+deadcode(void)
+{
+	if(debug['v'])
+		Bprint(&bso, "%5.2f deadcode\n", cputime());
+
+	mark(lookup(INITENTRY, 0));
+	mark(lookup("sys·morestack", 0));
+
+	sweeplist(&firstp, &lastp);
+	sweeplist(&datap, &edatap);
+}
+
diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h
index 4a2c456..6382203 100644
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -88,6 +88,7 @@
 	Adr	to;
 	Prog	*forwd;
 	Prog*	link;
+	Prog*	dlink;
 	Prog*	pcond;	/* work on this */
 	vlong	pc;
 	int32	line;
@@ -120,6 +121,7 @@
 	int32	sig;
 	Sym*	link;
 	Prog*	text;
+	Prog*	data;
 };
 struct	Optab
 {
@@ -385,9 +387,9 @@
 double	cputime(void);
 void	datblk(int32, int32);
 void	ignoreoptfuncs(void);
-void	definetypestrings(void);
-void definetypesigs(void);
 void	deadcode(void);
+void	definetypestrings(void);
+void	definetypesigs(void);
 void	diag(char*, ...);
 void	dodata(void);
 void	doinit(void);
@@ -421,6 +423,7 @@
 void*	mysbrk(uint32);
 Prog*	newdata(Sym*, int, int, int);
 Prog*	newtext(Prog*, Sym*);
+void	nopout(Prog*);
 void	nuxiinit(void);
 void	objfile(char*);
 int	opsize(Prog*);
diff --git a/src/cmd/6l/list.c b/src/cmd/6l/list.c
index 0d85d38..000c6fa 100644
--- a/src/cmd/6l/list.c
+++ b/src/cmd/6l/list.c
@@ -50,6 +50,9 @@
 	Prog *p;
 
 	p = va_arg(fp->args, Prog*);
+	if(p == P)
+		return fmtstrcpy(fp, "<P>");
+
 	bigP = p;
 
 	snprint(str1, sizeof(str1), "(%ld)", p->line);
@@ -421,7 +424,7 @@
 	textstksiz = arg & 0xffffffffLL;
 	if(textstksiz & 0x80000000LL)
 		textstksiz = -(-textstksiz & 0xffffffffLL);
-		
+
 	textarg = (arg >> 32) & 0xffffffffLL;
 	if(textarg & 0x80000000LL)
 		textarg = 0;
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index d6af049..076809a 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -369,9 +369,9 @@
 		objfile(a);
 	}
 	ignoreoptfuncs();
-	// TODO(rsc): remove unused code and data
 	definetypestrings();
 	definetypesigs();
+	deadcode();
 
 	firstp = firstp->link;
 	if(firstp == P)
@@ -1068,11 +1068,16 @@
 		// If we've seen an AGLOBL that said this sym was DUPOK,
 		// ignore any more ADATA we see, which must be
 		// redefinitions.
-		if(p->from.sym != S && p->from.sym->dupok) {
+		s = p->from.sym;
+		if(s != S && s->dupok) {
 			if(debug['v'])
-				Bprint(&bso, "skipping %s in %s: dupok", p->from.sym->name, pn);
+				Bprint(&bso, "skipping %s in %s: dupok", s->name, pn);
 			goto loop;
 		}
+		if(s != S) {
+			p->dlink = s->data;
+			s->data = p;
+		}
 		if(edatap == P)
 			datap = p;
 		else
diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c
index 45617ac..6e0fd58 100644
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -42,6 +42,7 @@
 		Bprint(&bso, "%5.2f dodata\n", cputime());
 	Bflush(&bso);
 	for(p = datap; p != P; p = p->link) {
+		curtext = p;	// for diag messages
 		s = p->from.sym;
 		if(p->as == ADYNT || p->as == AINIT)
 			s->value = dtype;
@@ -851,6 +852,8 @@
 	p->from.sym = s;
 	p->from.offset = o;
 	p->to.type = D_CONST;
+	p->dlink = s->data;
+	s->data = p;
 	return p;
 }