From aa876ff4c20c8c30173057cd05ab8dc210dc1894 Mon Sep 17 00:00:00 2001
From: George Koehler <xkernigh@netscape.net>
Date: Sun, 15 Oct 2017 13:15:03 -0400
Subject: [PATCH] Fix reglap for procedures that use both sizes of reg_float.

After the RA phase of ego, a procedure may put single-word and
double-word values in the same reg_float.  Then ncg will use both
LOCAL and DLOCAL tokens at the same offset.

I add isregvar_size() to ncg.  It receives the size of the LOCAL or
DLOCAL token, and picks the register of the correct size.  This fixes
a problem where ncg got the wrong-size register and corrupted the
stack.  This problem caused one of my test programs to segfault from
stack underflow.

Also adjust how fixregvars() handles both sizes.
---
 mach/proto/ncg/regvar.c | 62 ++++++++++++++++++++++++++++-------------
 mach/proto/ncg/regvar.h |  5 ++++
 mach/proto/ncg/subr.c   |  4 ++-
 3 files changed, 51 insertions(+), 20 deletions(-)

diff --git a/mach/proto/ncg/regvar.c b/mach/proto/ncg/regvar.c
index f39134892..51909b082 100644
--- a/mach/proto/ncg/regvar.c
+++ b/mach/proto/ncg/regvar.c
@@ -102,40 +102,44 @@ tryreg(struct regvar *rvlp, int typ) {
 
 void
 fixregvars(int saveall) {
-	register struct regvar *rv;
-	register rvtyp,i;
+	struct reginfo *rp, *rp2;
+	struct regvar *rv;
+	int i, regno, rvtyp;
 	
 	swtxt();
 	i_regsave();	/* machine dependent initialization */
 	for (rvtyp=reg_any;rvtyp<=reg_float;rvtyp++) {
 	    for(i=0;i<nregvar[rvtyp];i++)
 		if (saveall) {
-			struct reginfo *rp;
 			rp= &machregs[rvnumbers[rvtyp][i]];
+#ifdef REGLAP
+			/*
+			 * A reg_float may have two sizes.  If so,
+			 * only save the larger size.
+			 */
+			if ((regno = rp->r_members[0]) != 0 &&
+			    machregs[regno].r_size > rp->r_size)
+				rp= &machregs[regno];
+#endif
 			regsave(codestrings[rp->r_repr],
 				    (long)-TEM_WSIZE,rp->r_size);
-#ifdef REGLAP
-			/* reg_float may have one subregister */
-			if (rp->r_members[0]!=0) {
-				rp= &machregs[rp->r_members[0]];
-				regsave(codestrings[rp->r_repr],
-					    (long)-TEM_WSIZE,rp->r_size);
-			}
-#endif
 		} else if(regassigned[rvtyp][i].ra_score>0) {
 			rv=regassigned[rvtyp][i].ra_rv;
-			rv->rv_reg=rvnumbers[rvtyp][i];
+			rv->rv_reg = regno = rvnumbers[rvtyp][i];
 			rv->rv_type = rvtyp;
 #ifdef REGLAP
-			/* reg_float may have alternate size */
-			if (machregs[rv->rv_reg].r_size != rv->rv_size) {
-				rv->rv_reg = machregs[rv->rv_reg].r_members[0];
-				assert(rv->rv_reg != 0);
-				assert(machregs[rv->rv_reg].r_size ==
-					   rv->rv_size);
+			/*
+			 * Change regno to match rv->rv_size, but
+			 * leave old regno in rv->rv_reg so that
+			 * isregvar_size() can handle both sizes.
+			 */
+			if (machregs[regno].r_size != rv->rv_size) {
+				regno = machregs[regno].r_members[0];
+				assert(regno != 0);
+				assert(machregs[regno].r_size == rv->rv_size);
 			}
 #endif
-			regsave(codestrings[machregs[rv->rv_reg].r_repr],
+			regsave(codestrings[machregs[regno].r_repr],
 				    rv->rv_off,rv->rv_size);
 		}
 	}
@@ -152,6 +156,26 @@ isregvar(long off) {
 	return(-1);
 }
 
+#ifdef REGLAP
+int
+isregvar_size(long off, int size) {
+	int regno = isregvar(off);
+	/*
+	 * A reg_float may have two sizes.  If this register has the
+	 * wrong size, then use the overlapping register.  A register
+	 * may switch sizes in the middle of a procedure.
+	 */
+	if (regno > 0 && machregs[regno].r_size != size) {
+		if (machregs[regno].r_size != size) {
+			regno = machregs[regno].r_members[0];
+			assert(regno != 0);
+			assert(machregs[regno].r_size == size);
+		}
+	}
+	return regno;
+}
+#endif /* REGLAP */
+
 int
 isregtyp(long off) {
 	register struct regvar *rvlp;
diff --git a/mach/proto/ncg/regvar.h b/mach/proto/ncg/regvar.h
index 2a66ddc4d..cd7da216f 100644
--- a/mach/proto/ncg/regvar.h
+++ b/mach/proto/ncg/regvar.h
@@ -26,5 +26,10 @@ struct regvar *linkreg(long, int, int, int);
 void tryreg(struct regvar *, int);
 void fixregvars(int);
 int isregvar(long);
+#ifdef REGLAP
+int isregvar_size(long, int);
+#else
+#define isregvar_size(off, size) isregvar(off)
+#endif
 int isregtyp(long);
 void unlinkregs(void);
diff --git a/mach/proto/ncg/subr.c b/mach/proto/ncg/subr.c
index 0c14fa410..0ce937ecb 100644
--- a/mach/proto/ncg/subr.c
+++ b/mach/proto/ncg/subr.c
@@ -121,7 +121,9 @@ instance(instno,token) register token_p token; {
 	case IN_D_DESCR:
 		compute(&enodes[inp->in_info[1]], &result);
 		assert(result.e_typ==EV_INT);
-		if ((regno=isregvar(result.e_v.e_con)) > 0) {
+		regno = isregvar_size(result.e_v.e_con,
+				      tokens[inp->in_info[0]].t_size);
+		if (regno > 0) {
 			token->t_token = -1;
 			token->t_att[0].ar = regno;
 			for (i=TOKENSIZE-1;i>0;i--)