shithub: mc

Download patch

ref: 63c188f625c3939ab31231e3c6775ba72d37a00e
parent: 65ec134c5fc370d6df3382edb4cc633bd7802e3e
author: S. Gilles <[email protected]>
date: Sat Jun 20 17:54:41 EDT 2020

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 *.

--- a/6/asm.h
+++ b/6/asm.h
@@ -160,6 +160,7 @@
 	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 @@
 	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 */
--- a/6/gengas.c
+++ b/6/gengas.c
@@ -344,6 +344,7 @@
 	is.envoff = fn->envoff;
 	is.globls = globls;
 	is.ret = fn->ret;
+	is.rettype = fn->rettype;
 	is.cfg = fn->cfg;
 	is.cwd = strdup(cwd);
 
--- a/6/genp9.c
+++ b/6/genp9.c
@@ -350,6 +350,7 @@
 	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);
--- a/6/isel.c
+++ b/6/isel.c
@@ -27,6 +27,10 @@
 	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 @@
 	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 @@
 	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, 8));
+		break;
+	case ArgSmallAggr_Int_Flt:
+		retloc1 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 0));
+		retloc2 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 8));
+		break;
+	case ArgSmallAggr_Flt_Int:
+		retloc1 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		retloc2 = coreg(intretregs[nextintretreg++], tymodepart(t, 0, 8));
+		break;
+	case ArgSmallAggr_Flt_Flt:
+		retloc1 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 0));
+		retloc2 = coreg(fltretregs[nextfltretreg++], tymodepart(t, 1, 8));
+		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 @@
 	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 @@
 		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 @@
 		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 @@
 	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)) {
--- a/6/simp.c
+++ b/6/simp.c
@@ -35,8 +35,8 @@
 	/* return handling */
 	Node *endlbl;
 	Node *ret;
+	ArgType rettype;
 	int hasenv;
-	int isbigret;
 
 	/* location handling */
 	Node **blobs;
@@ -1065,6 +1065,7 @@
 	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 @@
 		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 @@
 
 	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 @@
 		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 @@
 	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 @@
 	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;
--- a/6/typeinfo.c
+++ b/6/typeinfo.c
@@ -415,7 +415,7 @@
 
 	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 */