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.


Handling 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 %rxmm0d 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      |   2 +
 6/gengas.c   |   1 +
 6/genp9.c    |   1 +
 6/isel.c     | 192 ++++++++++++++++++++++++++++++++++++++++++++-------
 6/simp.c     |  86 +++++++++++++++++------
 6/typeinfo.c |   2 +-
 6 files changed, 237 insertions(+), 47 deletions(-)

diff --git a/6/asm.h b/6/asm.h
index fc0b4c64..1789f341 100644
--- a/6/asm.h
+++ b/6/asm.h
@@ -160,6 +160,7 @@ struct Func {
 	Htab *envoff;	/* Loc* -> int envoff map */
 	size_t stksz;	/* stack size */
 	Node *ret;	/* return value */
+	ArgType rettype;	/* how to actually get ret out */
 
 	Cfg  *cfg;	/* flow graph */
 	char isexport;	/* is this exported from the asm? */
@@ -193,6 +194,7 @@ struct Isel {
 	Asmbb *curbb;
 
 	Node *ret;          /* we store the return into here */
+	ArgType rettype;    /* how ret actually gets out of the function */
 	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 b1ef7cb9..b224300b 100644
--- a/6/gengas.c
+++ b/6/gengas.c
@@ -344,6 +344,7 @@ 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.cfg = fn->cfg;
 	is.cwd = strdup(cwd);
 
diff --git a/6/genp9.c b/6/genp9.c
index f366c269..5a689846 100644
--- a/6/genp9.c
+++ b/6/genp9.c
@@ -350,6 +350,7 @@ 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.cfg = fn->cfg;
 	if (fn->hasenv)
 		is.envp = locreg(ModeQ);
diff --git a/6/isel.c b/6/isel.c
index 33c3c381..00d1f0b2 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 fltretregs[] = {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)
 {
@@ -629,43 +653,86 @@ plus8(Isel *s, Loc *base)
 	return locmem(8, forcedreg, NULL, ModeQ);
 }
 
-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 nfloats, nints;
-	Loc *retloc, *rsp, *ret;	/* hard-coded registers */
+	Loc *retloc1, *retloc2, *rsp;	/* hard-coded registers */
+	Loc *ret;
+	size_t nextintretreg = 0, nextfltretreg = 0;
 	Loc *stkbump;	/* calculated stack offset */
 	Type *t, *fn;
 	Node **args;
+	Node *retnode;
+	ArgType rettype;
 	size_t i;
 	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));
+	retloc1 = NULL;
+	retloc2 = NULL;
+	rettype = classify(t);
+
+	switch (rettype) {
+	case ArgVoid:
+	case ArgBig:
+		break;
+	case ArgReg:
+		if (istyfloat(t)) {
+			retloc1 = coreg(Rxmm0d, mode(n));
+		} else {
+			retloc1 = coreg(Rrax, mode(n));
+		}
+		break;
+	case ArgSmallAggr_Int:
+		retloc1 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 0));
+		break;
+	case ArgSmallAggr_Flt:
+		retloc1 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		break;
+	case ArgSmallAggr_Int_Int:
+		retloc1 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 0));
+		retloc2 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 0));
+		break;
+	case ArgSmallAggr_Int_Flt:
+		retloc1 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 0));
+		retloc2 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		break;
+	case ArgSmallAggr_Flt_Int:
+		retloc1 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		retloc2 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 0));
+		break;
+	case ArgSmallAggr_Flt_Flt:
+		retloc1 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		retloc2 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		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 != ArgVoid) {
+		retnode = n->expr.args[falseargs];
+		if (rettype != ArgBig) {
+			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.
@@ -746,13 +813,46 @@ 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 ArgVoid:
+	case ArgBig:
+		/*
+		 * No need to do anything. The return location, if any, was taken care of
+		 * as the hidden argument.
+		 */
+		break;
+	case ArgReg:
+		/* 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 ArgSmallAggr_Int:
+		g(s, Imov, retloc1, locmem(0, inri(s, selexpr(s, retnode)), NULL, ModeQ), NULL);
+		break;
+	case ArgSmallAggr_Flt:
+		g(s, Imovs, retloc1, locmem(0, inri(s, selexpr(s, retnode)), NULL, ModeD), NULL);
+		break;
+	case ArgSmallAggr_Int_Int:
+		g(s, Imov, retloc1, locmem(0, inri(s, selexpr(s, retnode)), NULL, ModeQ), NULL);
+		g(s, Imov, retloc2, locmem(8, inri(s, selexpr(s, retnode)), NULL, ModeQ), NULL);
+		break;
+	case ArgSmallAggr_Int_Flt:
+		g(s, Imov,  retloc1, locmem(0, inri(s, selexpr(s, retnode)), NULL, ModeQ), NULL);
+		g(s, Imovs, retloc2, locmem(8, inri(s, selexpr(s, retnode)), NULL, ModeD), NULL);
+		break;
+	case ArgSmallAggr_Flt_Int:
+		g(s, Imovs, retloc1, locmem(0, inri(s, selexpr(s, retnode)), NULL, ModeD), NULL);
+		g(s, Imov,  retloc2, locmem(8, inri(s, selexpr(s, retnode)), NULL, ModeQ), NULL);
+		break;
+	case ArgSmallAggr_Flt_Flt:
+		g(s, Imovs, retloc1, locmem(0, inri(s, selexpr(s, retnode)), NULL, ModeD), NULL);
+		g(s, Imovs, retloc2, locmem(8, inri(s, selexpr(s, retnode)), NULL, ModeD), NULL);
+		break;
 	}
-	return ret;
 }
 
 static Loc*
@@ -892,7 +992,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);
@@ -990,7 +1090,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]);
@@ -1278,16 +1378,56 @@ epilogue(Isel *s)
 	Loc *rsp, *rbp;
 	Loc *ret;
 	size_t i;
+	size_t nextintretreg = 0, nextfltretreg = 0;
 
 	rsp = locphysreg(Rrsp);
 	rbp = locphysreg(Rrbp);
-	if (s->ret) {
+	switch (s->rettype) {
+	case ArgVoid: break;
+	case ArgReg:
+		/* 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 ArgBig:
+		/* 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 ArgSmallAggr_Int:
+		/* s->ret is an address, and will be returned as values */
+		ret = loc(s, s->ret);
+		load(s, locmem(0, ret, NULL, ModeQ), coreg(intretregs[nextintretreg++], ModeQ));
+		break;
+	case ArgSmallAggr_Flt:
+		ret = loc(s, s->ret);
+		load(s, locmem(0, ret, NULL, ModeD), coreg(fltretregs[nextfltretreg++], ModeD));
+		break;
+	case ArgSmallAggr_Int_Int:
+		ret = loc(s, s->ret);
+		load(s, locmem(0, ret, NULL, ModeQ), coreg(intretregs[nextintretreg++], ModeQ));
+		load(s, locmem(8, ret, NULL, ModeQ), coreg(intretregs[nextintretreg++], ModeQ));
+		break;
+	case ArgSmallAggr_Int_Flt:
+		ret = loc(s, s->ret);
+		load(s, locmem(0, ret, NULL, ModeQ), coreg(intretregs[nextintretreg++], ModeQ));
+		load(s, locmem(8, ret, NULL, ModeD), coreg(fltretregs[nextfltretreg++], ModeD));
+		break;
+	case ArgSmallAggr_Flt_Int:
+		ret = loc(s, s->ret);
+		load(s, locmem(0, ret, NULL, ModeD), coreg(fltretregs[nextfltretreg++], ModeD));
+		load(s, locmem(8, ret, NULL, ModeQ), coreg(intretregs[nextintretreg++], ModeQ));
+		break;
+	case ArgSmallAggr_Flt_Flt:
+		ret = loc(s, s->ret);
+		load(s, locmem(0, ret, NULL, ModeD), coreg(fltretregs[nextfltretreg++], ModeD));
+		load(s, locmem(8, ret, NULL, ModeD), coreg(fltretregs[nextfltretreg++], ModeD));
+		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..74b9c487 100644
--- a/6/simp.c
+++ b/6/simp.c
@@ -35,8 +35,8 @@ Simp {
 	/* return handling */
 	Node *endlbl;
 	Node *ret;
+	ArgType rettype;
 	int hasenv;
-	int isbigret;
 
 	/* location handling */
 	Node **blobs;
@@ -1065,6 +1065,7 @@ simpcall(Simp *s, Node *n, Node *dst)
 	size_t i, nargs;
 	Node **args;
 	Type *ft;
+	ArgType rettype;
 	Op op;
 
 	/* NB: If we called rval() on a const function, we would end up with
@@ -1089,8 +1090,23 @@ simpcall(Simp *s, Node *n, Node *dst)
 		lappend(&args, &nargs, getenvptr(s, fn));
 	}
 
-	if (exprtype(n)->type != Tyvoid && isstacktype(exprtype(n)))
+	rettype = classify(exprtype(n));
+	switch (rettype) {
+	case ArgVoid:
+		break;
+	case ArgBig:
+	case ArgSmallAggr_Int:
+	case ArgSmallAggr_Flt:
+	case ArgSmallAggr_Int_Int:
+	case ArgSmallAggr_Int_Flt:
+	case ArgSmallAggr_Flt_Int:
+	case ArgSmallAggr_Flt_Flt:
 		lappend(&args, &nargs, addr(s, r, exprtype(n)));
+		break;
+	case ArgReg:
+		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 +1126,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 +1249,40 @@ 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 ArgBig
+		 * and ArgReg, exfiltrate the value from the function. In the case of
+		 * ArgSmallAggr_XYZ, put a pointer to the value where the function
+		 * epilogue can access it.
+		 */
+		switch (s->rettype) {
+		case ArgSmallAggr_Int:
+		case ArgSmallAggr_Flt:
+		case ArgSmallAggr_Int_Int:
+		case ArgSmallAggr_Int_Flt:
+		case ArgSmallAggr_Flt_Int:
+		case ArgSmallAggr_Flt_Flt:
+			t = s->ret;
+			u = rval(s, args[0], NULL);
+			u = addr(s, u, exprtype(args[0]));
+			v = set(t, u);
+			append(s, v);
+		case ArgBig:
 			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 ArgVoid:
+			rval(s, args[0], NULL);
+			break;
+		case ArgReg:
 			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 +1403,30 @@ simpinit(Simp *s, Node *f)
 	s->nstmts = 0;
 	s->stmts = NULL;
 	s->endlbl = genlbl(f->loc);
-	s->ret = NULL;
+	s->rettype = ArgVoid;
 
 	/* make a temp for the return type */
 	ty = f->func.type->sub[0];
-	if (isstacktype(ty)) {
-		s->isbigret = 1;
+	s->rettype = classify(ty);
+
+	switch(s->rettype) {
+	case ArgVoid:
+		break;
+	case ArgSmallAggr_Int:
+	case ArgSmallAggr_Flt:
+	case ArgSmallAggr_Int_Int:
+	case ArgSmallAggr_Int_Flt:
+	case ArgSmallAggr_Flt_Int:
+	case ArgSmallAggr_Flt_Flt:
+		s->ret = gentemp(f->loc, mktyptr(f->loc, ty), &dcl);
+		break;
+	case ArgBig:
 		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 ArgReg:
+		s->ret = gentemp(f->loc, ty, NULL);
+		break;
 	}
 
 	for (i = 0; i < f->func.nargs; i++) {
@@ -1486,6 +1531,7 @@ simpfn(Simp *s, char *name, Node *dcl)
 	fn->stkoff = s->stkoff;
 	fn->envoff = s->envoff;
 	fn->ret = s->ret;
+	fn->rettype = s->rettype;
 	fn->args = s->args;
 	fn->nargs = s->nargs;
 	fn->cfg = cfg;
diff --git a/6/typeinfo.c b/6/typeinfo.c
index fdced668..b3810165 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 (classify(t->sub[0]) == ArgBig)
 		nargs++;
 	/* valists are replaced with hidden type parameter,
 	 * which we want on the stack for ease of ABI */
-- 
2.27.0


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