Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 6/9] Return small-aggregate values by the AMD64 abi.


The returned value from a function call is now handled in four ways.

 - Functions that return void do nothing.

 - Functions that return in a single register are provided an extra
   argument by simpcall: the Node to which the return value should be
   stored. gencall handles moving %rax or %xmm0d to that node as
   appropriate.

 - Functions that return large aggregates are provided an extra argument
   by simpcall: the address of the Node to which the return value should
   be stored. This is used as the hidden argument, as previously.

 - Functions that return small aggregates are also provided the address
   of the Node for the return value, but this is not used as a hidden
   argument. Instead, gencall uses this to know where to store the
   values in the return registers.

Since gencall now handles the assignment of the returned value, gencall
does not return a Loc *.
---
 6/asm.h      |   4 ++
 6/gengas.c   |   3 +
 6/genp9.c    |   3 +
 6/isel.c     | 200 +++++++++++++++++++++++++++++++++++++++++++--------
 6/simp.c     |  76 ++++++++++++++------
 6/typeinfo.c |   2 +-
 6 files changed, 239 insertions(+), 49 deletions(-)

diff --git a/6/asm.h b/6/asm.h
index 74a019e0..57fdba5e 100644
--- a/6/asm.h
+++ b/6/asm.h
@@ -155,6 +155,8 @@ struct Func {
 	Htab *envoff;	/* Loc* -> int envoff map */
 	size_t stksz;	/* stack size */
 	Node *ret;	/* return value */
+	RetType rettype;	/* how to actually get ret out */
+	PassIn pc[2];	/* only if rettype is RetSmallAggregate */
 
 	Cfg  *cfg;	/* flow graph */
 	char isexport;	/* is this exported from the asm? */
@@ -188,6 +190,8 @@ struct Isel {
 	Asmbb *curbb;
 
 	Node *ret;          /* we store the return into here */
+	RetType rettype;    /* how ret actually gets out of the function */
+	PassIn pc[2];       /* only if rettype is RetSmallAggregate */
 	Htab *spillslots;   /* reg id  => int stkoff */
 	Htab *reglocs;      /* decl id => Loc *reg */
 	Htab *stkoff;       /* decl id => int stkoff */
diff --git a/6/gengas.c b/6/gengas.c
index 991e30b7..8769216f 100644
--- a/6/gengas.c
+++ b/6/gengas.c
@@ -357,6 +357,9 @@ genfunc(FILE *fd, Func *fn, Htab *globls, Htab *strtab)
 	is.envoff = fn->envoff;
 	is.globls = globls;
 	is.ret = fn->ret;
+	is.rettype = fn->rettype;
+	is.pc[0] = fn->pc[0];
+	is.pc[1] = fn->pc[1];
 	is.cfg = fn->cfg;
 	is.cwd = strdup(cwd);
 
diff --git a/6/genp9.c b/6/genp9.c
index 32af6de3..fda42d81 100644
--- a/6/genp9.c
+++ b/6/genp9.c
@@ -360,6 +360,9 @@ genfunc(FILE *fd, Func *fn, Htab *globls, Htab *strtab)
 	is.envoff = fn->envoff;
 	is.globls = globls;
 	is.ret = fn->ret;
+	is.rettype = fn->rettype;
+	is.pc[0] = fn->pc[0];
+	is.pc[1] = fn->pc[1];
 	is.cfg = fn->cfg;
 	if (fn->hasenv)
 		is.envp = locreg(ModeQ);
diff --git a/6/isel.c b/6/isel.c
index 3a0129c6..358b0fa0 100644
--- a/6/isel.c
+++ b/6/isel.c
@@ -27,6 +27,10 @@ regid floatargregs[] = {
 	Rxmm4d, Rxmm5d, Rxmm6d, Rxmm7d,
 };
 regid intargregs[] = {Rrdi, Rrsi, Rrdx, Rrcx, Rr8, Rr9};
+#define Nfloatregrets 2
+#define Nintregrets 2
+regid floatretregs[] = {Rxmm0d, Rxmm1d};
+regid intretregs[] = {Rrax, Rrdx};
 
 /* used to decide which operator is appropriate
  * for implementing various conditional operators */
@@ -83,6 +87,26 @@ tymode(Type *t)
 	return ModeNone;
 }
 
+static Mode tymodepart(Type *t, int is_float, size_t displacement)
+{
+	assert(isstacktype(t));
+	size_t sz = tysize(t);
+
+	if (is_float) {
+		switch(sz - displacement) {
+		case 4: return ModeF; break;
+		default: return ModeD; break;
+		}
+	} else {
+		switch(sz - displacement) {
+		case 1: return ModeB; break;
+		case 2: return ModeW; break;
+		case 4: return ModeL; break;
+		default: return ModeQ; break;
+		}
+	}
+}
+
 static Mode
 forcefltmode(Mode m)
 {
@@ -606,44 +630,92 @@ sufficientregs(PassIn *pc, size_t sz, size_t nfloats, size_t nints)
 	return (needed_floats + nfloats <= Nfloatregargs) && (needed_ints + nints <= Nintregargs);
 }
 
-static Loc *
+static void
 gencall(Isel *s, Node *n)
 {
 	Loc *arg;	/* values we reduced */
-	size_t argsz, argoff, nargs, vasplit;
+	size_t argsz, argoff, nargs, falseargs, vasplit;
 	size_t sz;
 	size_t nfloats, nints;
-	Loc *retloc, *rsp, *ret;	/* hard-coded registers */
+	Loc *retloc1, *retloc2, *rsp;	/* hard-coded registers */
+	Loc *ret, *temp;
+	size_t nextretintreg, nextretfltreg;
 	Loc *stkbump;	/* calculated stack offset */
 	Type *t, *fn;
 	Node **args;
+	Node *retnode;
+	RetType rettype;
 	size_t i;
+	PassIn pc[2] = { PassInNoPref, PassInNoPref };
 	int vararg;
 
 	rsp = locphysreg(Rrsp);
+
 	t = exprtype(n);
-	if (tybase(t)->type == Tyvoid || isstacktype(t)) {
-		retloc = NULL;
-		ret = NULL;
-	} else if (istyfloat(t)) {
-		retloc = coreg(Rxmm0d, mode(n));
-		ret = locreg(mode(n));
-	} else {
-		retloc = coreg(Rrax, mode(n));
-		ret = locreg(mode(n));
+	sz = tysize(t);
+	retloc1 = NULL;
+	retloc2 = NULL;
+	rettype = howreturned(t);
+
+	switch (rettype) {
+	case RetVoid:
+	case RetBig:
+		break;
+	case RetReg:
+		if (istyfloat(t)) {
+			retloc1 = coreg(Rxmm0d, mode(n));
+		} else {
+			retloc1 = coreg(Rrax, mode(n));
+		}
+		break;
+	case RetSmallAggregate:
+		nextretintreg = 0;
+		nextretfltreg = 0;
+		classify(t, pc);
+		switch (pc[0]) {
+		case PassInInt:
+			retloc1 = coreg(intretregs[nextretintreg++], tymodepart(t, 0, 0));
+			break;
+		case PassInSSE:
+			retloc1 = coreg(floatretregs[nextretfltreg++], tymodepart(t, 1, 0));
+			break;
+		default: die("don't know how to return value");
+		}
+		if (sz > 8) {
+			switch (pc[1]) {
+			case PassInInt:
+				retloc2 = coreg(intretregs[nextretintreg++], tymodepart(t, 0, 8));
+				break;
+			case PassInSSE:
+				retloc2 = coreg(floatretregs[nextretfltreg++], tymodepart(t, 1, 8));
+				break;
+			default: die("don't know how to return value");
+			}
+		}
+		break;
 	}
+
 	fn = tybase(exprtype(n->expr.args[0]));
 	/* calculate the number of args we expect to see, adjust
 	 * for a hidden return argument. */
 	vasplit = countargs(fn);
 	argsz = 0;
-	if (exprop(n) == Ocall) {
-		args = &n->expr.args[1];
-		nargs = n->expr.nargs - 1;
-	} else {
-		args = &n->expr.args[2];
-		nargs = n->expr.nargs - 2;
+
+	/*
+	 * { the function itself, [optional environment], [optional return information], real arg 1, ... }
+	 */
+	falseargs = 1;
+	if (exprop(n) == Ocallind) {
+		falseargs++;
+	}
+	if (rettype != RetVoid) {
+		retnode = n->expr.args[falseargs];
+		if (rettype != RetBig) {
+			falseargs++;
+		}
 	}
+	args = &n->expr.args[falseargs];
+	nargs = n->expr.nargs - falseargs;
 	/* Have to calculate the amount to bump the stack
 	 * pointer by in one pass first, otherwise if we push
 	 * one at a time, we evaluate the args in reverse order.
@@ -680,7 +752,6 @@ gencall(Isel *s, Node *n)
 		else
 			argoff = align(argoff, 8);
 		if (!vararg && isaggregate(t) && sz <= 16) {
-			PassIn pc[2] = { PassInNoPref, PassInNoPref };
 			classify(t, pc);
 			if (pc[0] == PassInMemory || pc[1] == PassInMemory || !sufficientregs(pc, sz, nfloats, nints)) {
 				placearg(s, args[i], arg, PassInMemory, rsp, vararg, &nfloats, &nints, &argoff);
@@ -707,13 +778,44 @@ gencall(Isel *s, Node *n)
 	call(s, n);
 	if (argsz)
 		g(s, Iadd, stkbump, rsp, NULL);
-	if (retloc) {
-		if (isfloatmode(retloc->mode))
-			g(s, Imovs, retloc, ret, NULL);
+
+	switch (rettype) {
+	case RetVoid:
+	case RetBig:
+		/*
+		 * No need to do anything. The return location, if any, was taken care of
+		 * as the hidden argument.
+		 */
+		break;
+	case RetReg:
+		/* retnode is the actual thing we're storing in */
+		ret = varloc(s, retnode);
+		if (isfloatmode(retloc1->mode))
+			g(s, Imovs, retloc1, ret, NULL);
 		else
-			g(s, Imov, retloc, ret, NULL);
+			g(s, Imov, retloc1, ret, NULL);
+		break;
+	case RetSmallAggregate:
+		/* retnode is the address of the memory to store in */
+		ret = selexpr(s, retnode);
+		ret = inri(s, ret);
+		if (retloc1) {
+			temp = locmem(0, ret, NULL, ModeQ);
+			if (isfloatmode(retloc1->mode))
+				g(s, Imovs, retloc1, temp, NULL);
+			else
+				g(s, Imov, retloc1, temp, NULL);
+		}
+
+		if (retloc2) {
+			temp = locmem(8, ret, NULL, ModeQ);
+			if (isfloatmode(retloc2->mode))
+				g(s, Imovs, retloc2, temp, NULL);
+			else
+				g(s, Imov, retloc2, temp, NULL);
+		}
+		break;
 	}
-	return ret;
 }
 
 static Loc*
@@ -853,7 +955,7 @@ selexpr(Isel *s, Node *n)
 		if (mode(args[0]) == ModeF) {
 			a = locreg(ModeF);
 			b = loclit(1LL << (31), ModeF);
-			g(s, Imovs, r, a);
+			g(s, Imovs, r, a, NULL);
 		} else if (mode(args[0]) == ModeD) {
 			a = locreg(ModeQ);
 			b = loclit(1LL << 63, ModeQ);
@@ -951,7 +1053,7 @@ selexpr(Isel *s, Node *n)
 		break;
 	case Ocall:
 	case Ocallind:
-		r = gencall(s, n);
+		gencall(s, n);
 		break;
 	case Oret:
 		a = locstrlbl(s->cfg->end->lbls[0]);
@@ -1230,18 +1332,60 @@ static void
 epilogue(Isel *s)
 {
 	Loc *rsp, *rbp;
-	Loc *ret;
+	Loc *ret, *temp;
 	size_t i;
+	size_t nextretintreg, nextretfltreg;
 
 	rsp = locphysreg(Rrsp);
 	rbp = locphysreg(Rrbp);
-	if (s->ret) {
+	switch (s->rettype) {
+	case RetVoid: break;
+	case RetReg:
+		/* s->ret is a value, and will be returned that way */
 		ret = loc(s, s->ret);
 		if (istyfloat(exprtype(s->ret)))
 			g(s, Imovs, ret, coreg(Rxmm0d, ret->mode), NULL);
 		else
 			g(s, Imov, ret, coreg(Rax, ret->mode), NULL);
+		break;
+	case RetBig:
+		/* s->ret is an address, and will be returned that way */
+		ret = loc(s, s->ret);
+		g(s, Imov, ret, coreg(Rax, ret->mode), NULL);
+		break;
+	case RetSmallAggregate:
+		/* s->ret is an address, and will be returned as values */
+		ret = loc(s, s->ret);
+		nextretintreg = 0;
+		nextretfltreg = 0;
+		switch(s->pc[0]) {
+		case PassInInt:
+			temp = locmem(0, ret, NULL, ModeQ);
+			load(s, temp, coreg(intretregs[nextretintreg++], ModeQ));
+			break;
+		case PassInSSE:
+			temp = locmem(0, ret, NULL, ModeD);
+			load(s, temp, coreg(floatretregs[nextretfltreg++], ModeD));
+			break;
+		default: die("impossible passing of small aggregate");
+		}
+
+		if (s->pc[1] != PassInNoPref) {
+			switch(s->pc[1]) {
+			case PassInInt:
+				temp = locmem(8, ret, NULL, ModeQ);
+				load(s, temp, coreg(intretregs[nextretintreg++], ModeQ));
+				break;
+			case PassInSSE:
+				temp = locmem(8, ret, NULL, ModeD);
+				load(s, temp, coreg(floatretregs[nextretfltreg++], ModeD));
+				break;
+			default: die("impossible passing of small aggregate");
+			}
+		}
+		break;
 	}
+
 	/* restore registers */
 	for (i = 0; savedregs[i] != Rnone; i++) {
 		if (isfloatmode(s->calleesave[i]->mode)) {
diff --git a/6/simp.c b/6/simp.c
index d175e4e2..78513ce9 100644
--- a/6/simp.c
+++ b/6/simp.c
@@ -35,8 +35,9 @@ Simp {
 	/* return handling */
 	Node *endlbl;
 	Node *ret;
+	RetType rettype;
+	PassIn pc[2];
 	int hasenv;
-	int isbigret;
 
 	/* location handling */
 	Node **blobs;
@@ -1065,6 +1066,7 @@ simpcall(Simp *s, Node *n, Node *dst)
 	size_t i, nargs;
 	Node **args;
 	Type *ft;
+	RetType rettype;
 	Op op;
 
 	/* NB: If we called rval() on a const function, we would end up with
@@ -1089,8 +1091,18 @@ simpcall(Simp *s, Node *n, Node *dst)
 		lappend(&args, &nargs, getenvptr(s, fn));
 	}
 
-	if (exprtype(n)->type != Tyvoid && isstacktype(exprtype(n)))
+	rettype = howreturned(exprtype(n));
+	switch (rettype) {
+	case RetVoid:
+		break;
+	case RetBig:
+	case RetSmallAggregate:
 		lappend(&args, &nargs, addr(s, r, exprtype(n)));
+		break;
+	case RetReg:
+		lappend(&args, &nargs, r);
+		break;
+	}
 
 	for (i = 1; i < n->expr.nargs; i++) {
 		if (i < ft->nsub && tybase(ft->sub[i])->type == Tyvalist)
@@ -1110,11 +1122,7 @@ simpcall(Simp *s, Node *n, Node *dst)
 
 	call = mkexprl(n->loc, op, args, nargs);
 	call->expr.type = exprtype(n);
-	if (r && !isstacktype(exprtype(n))) {
-		append(s, set(r, call));
-	} else {
-		append(s, call);
-	}
+	append(s, call);
 	return r;
 }
 
@@ -1237,20 +1245,35 @@ rval(Simp *s, Node *n, Node *dst)
 		fatal(n, "'_' may not be an rvalue");
 		break;
 	case Oret:
-		if (s->isbigret) {
+		/*
+		 * Compute and put the correct value into s->ret. In the case of RetBig
+		 * and RetReg, exfiltrate the value from the function. In the case of
+		 * RetSmallAggregate, put a pointer to the value where the function
+		 * epilogue can access it.
+		 */
+		switch (s->rettype) {
+		case RetSmallAggregate:
+			t = s->ret;
+			u = rval(s, args[0], NULL);
+			u = addr(s, u, exprtype(args[0]));
+			v = set(t, u);
+			append(s, v);
+		case RetBig:
 			t = rval(s, args[0], NULL);
 			t = addr(s, t, exprtype(args[0]));
 			u = disp(n->loc, size(args[0]));
 			v = mkexpr(n->loc, Oblit, s->ret, t, u, NULL);
 			append(s, v);
-		} else {
+			break;
+		case RetVoid:
+			rval(s, args[0], NULL);
+			break;
+		case RetReg:
 			t = s->ret;
 			u = rval(s, args[0], NULL);
-			/* void calls return nothing */
-			if (t) {
-				t = set(t, u);
-				append(s, t);
-			}
+			t = set(t, u);
+			append(s, t);
+			break;
 		}
 		append(s, mkexpr(n->loc, Oret, NULL));
 		break;
@@ -1371,17 +1394,27 @@ simpinit(Simp *s, Node *f)
 	s->nstmts = 0;
 	s->stmts = NULL;
 	s->endlbl = genlbl(f->loc);
-	s->ret = NULL;
+	s->rettype = RetVoid;
 
 	/* make a temp for the return type */
 	ty = f->func.type->sub[0];
-	if (isstacktype(ty)) {
-		s->isbigret = 1;
+	s->rettype = howreturned(ty);
+	if (s->rettype == RetSmallAggregate) {
+		classify(ty, s->pc);
+	}
+	switch(s->rettype) {
+	case RetVoid:
+		break;
+	case RetSmallAggregate:
+		s->ret = gentemp(f->loc, mktyptr(f->loc, ty), &dcl);
+		break;
+	case RetBig:
 		s->ret = gentemp(f->loc, mktyptr(f->loc, ty), &dcl);
 		declarearg(s, dcl);
-	} else if (tybase(ty)->type != Tyvoid) {
-		s->isbigret = 0;
-		s->ret = gentemp(f->loc, ty, &dcl);
+		break;
+	case RetReg:
+		s->ret = gentemp(f->loc, ty, NULL);
+		break;
 	}
 
 	for (i = 0; i < f->func.nargs; i++) {
@@ -1486,6 +1519,9 @@ simpfn(Simp *s, char *name, Node *dcl)
 	fn->stkoff = s->stkoff;
 	fn->envoff = s->envoff;
 	fn->ret = s->ret;
+	fn->rettype = s->rettype;
+	fn->pc[0] = s->pc[0];
+	fn->pc[1] = s->pc[1];
 	fn->args = s->args;
 	fn->nargs = s->nargs;
 	fn->cfg = cfg;
diff --git a/6/typeinfo.c b/6/typeinfo.c
index aff1c338..ab428ad3 100644
--- a/6/typeinfo.c
+++ b/6/typeinfo.c
@@ -415,7 +415,7 @@ countargs(Type *t)
 
 	t = tybase(t);
 	nargs = t->nsub - 1;
-	if (isstacktype(t->sub[0]))
+	if (isstacktype(t->sub[0]) && howreturned(t->sub[0]) == RetBig)
 		nargs++;
 	/* valists are replaced with hidden type parameter,
 	 * which we want on the stack for ease of ABI */
-- 
2.26.2


References:
[PATCH 0/9] Handle small-aggregates via AMD64 abi"S. Gilles" <sgilles@xxxxxxx>