shithub: mc

Download patch

ref: dfc145534768f3b461d92c929931bb3b48237bdb
parent: cba8dceb7246552cc446b60cef4d5a3069907faf
author: S. Gilles <sgilles@umd.edu>
date: Sat May 23 01:58:39 EDT 2020

Add classification algorithm for small-struct passing.

This is meant to implement section 3.2.3 of the AMD64 abi draft. Types
which are meant to inter-operate with C should be passed in ways that C
will expect.

 - uint16, flt32, bool, &c. should map to the obvious things

 - myrddin structs should map to C structs

 - myrddin tuples could map to C structs, so are treated as structs with
   unnamed fields.

 - myrddin unions that are classified as enums are treated as ints.

Slices and (non-enum) unions are always passed on the stack, regardless of size,
and therefore do not inter-operate with C.

 - myrddin slices could map to struct { type *data; size_t len; }
   transparently. However, this would require a lot of work on the
   myrddin rt code, and would incur a minor performance penalty in the
   common case of simply passing a slice as an argument in myrddin-only
   code. Let's hold off on completely overhauling the rt code until a
   compelling use case for magical slice arguments appears.

 - myrddin (non-enum) unions are not mapped to anything right now. They
   could map to struct { union {...} data; int tag; }, though there are
   special cases to handle with std.option(@a#).

--- a/6/asm.h
+++ b/6/asm.h
@@ -46,6 +46,20 @@
 } Mode;
 
 typedef enum {
+	PassInNoPref,
+	PassInSSE,
+	PassInInt,
+	PassInMemory,
+} PassIn;
+
+typedef enum {
+	RetVoid,
+	RetReg,
+	RetSmallAggregate,
+	RetBig,
+} RetType;
+
+typedef enum {
 	Classbad,
 	Classint,
 	Classflt,
@@ -316,7 +330,10 @@
 size_t size(Node *n);
 ssize_t tyoffset(Type *ty, Node *memb);
 ssize_t offset(Node *aggr, Node *memb);
+size_t countargs(Type *t);
+void classify(Type *t, PassIn *p);
 int isaggregate(Type *t);
+RetType howreturned(Type *t);
 int stacknode(Node *n);
 int floatnode(Node *n);
 void breakhere();
--- a/6/isel.c
+++ b/6/isel.c
@@ -500,22 +500,6 @@
 	g(s, op, f, NULL);
 }
 
-static size_t
-countargs(Type *t)
-{
-	size_t nargs;
-
-	t = tybase(t);
-	nargs = t->nsub - 1;
-	if (isstacktype(t->sub[0]))
-		nargs++;
-	/* valists are replaced with hidden type parameter,
-	 * which we want on the stack for ease of ABI */
-	if (tybase(t->sub[t->nsub - 1])->type == Tyvalist)
-		nargs--;
-	return nargs;
-}
-
 static void
 placearg(Isel *s, Node *argn, Loc *argloc, Loc *rsp, int vararg, size_t *nfloats, size_t *nints, size_t *argoff)
 {
--- a/6/typeinfo.c
+++ b/6/typeinfo.c
@@ -408,6 +408,139 @@
 	return tyoffset(exprtype(aggr), memb);
 }
 
+size_t
+countargs(Type *t)
+{
+	size_t nargs;
+
+	t = tybase(t);
+	nargs = t->nsub - 1;
+	if (isstacktype(t->sub[0]))
+		nargs++;
+	/* valists are replaced with hidden type parameter,
+	 * which we want on the stack for ease of ABI */
+	if (tybase(t->sub[t->nsub - 1])->type == Tyvalist)
+		nargs--;
+	return nargs;
+}
+
+static void join_classification(PassIn *current, PassIn new)
+{
+	if (*current == PassInNoPref) {
+		*current = new;
+	} else if ((*current == PassInInt) || (new == PassInInt)) {
+		*current = PassInInt;
+	} else if (*current != new) {
+		*current = PassInMemory;
+	}
+}
+
+static void
+classify_recursive(Type *t, PassIn *p, size_t *total_offset)
+{
+	size_t i = 0, sz = tysize(t);
+	size_t cur_offset = *total_offset;
+	PassIn *cur = 0;
+
+	if (!t)
+		die("cannot pass empty type.");
+	if (cur_offset + sz > 16) {
+		p[0] = PassInMemory;
+		p[1] = PassInMemory;
+		return;
+	}
+	cur = &p[cur_offset / 8];
+
+	switch(t->type) {
+	case Tyvoid: break;
+	case Tybool:
+	case Tybyte:
+	case Tychar:
+	case Tyint:
+	case Tyint16:
+	case Tyint32:
+	case Tyint64:
+	case Tyint8:
+	case Typtr:
+	case Tyuint:
+	case Tyuint16:
+	case Tyuint32:
+	case Tyuint64:
+	case Tyuint8:
+		join_classification(cur, PassInInt);
+		break;
+	case Tyslice:
+		/* Slices are too myrddin-specific, they go on the stack. */
+		join_classification(&p[0], PassInMemory);
+		join_classification(&p[1], PassInMemory);
+		break;
+	case Tyflt32:
+	case Tyflt64:
+		join_classification(cur, PassInSSE);
+		break;
+	case Tyname:
+		classify_recursive(t->sub[0], p, total_offset);
+		break;
+	case Tybad:
+	case Tycode:
+	case Tyfunc:
+	case Tygeneric:
+	case Typaram:
+	case Tyunres:
+	case Tyvalist:
+	case Tyvar:
+	case Ntypes:
+		/* We shouldn't even be in this function */
+		join_classification(cur, PassInMemory);
+		break;
+	case Tytuple:
+		for (i = 0; i < t->nsub; ++i) {
+			*total_offset = alignto(*total_offset, t->sub[i]);
+			classify_recursive(t->sub[i], p, total_offset);
+		}
+		*total_offset = alignto(*total_offset, t);
+		break;
+	case Tystruct:
+		for (i = 0; i < t->nmemb; ++i) {
+			Type *fieldt = decltype(t->sdecls[i]);
+			*total_offset = alignto(*total_offset, fieldt);
+			classify_recursive(fieldt, p, total_offset);
+		}
+		*total_offset = alignto(*total_offset, t);
+		break;
+	case Tyunion:
+		/*
+		 * General enums are too complicated to interop with C, which is the only
+		 * reason for anything other than PassInMemory.
+		 */
+		if (isenum(t))
+			join_classification(cur, PassInInt);
+		else
+			join_classification(cur, PassInMemory);
+		break;
+	case Tyarray:
+		if (t->asize) {
+			t->asize = fold(t->asize, 1);
+			assert(exprop(t->asize) == Olit);
+			for (i = 0; i < t->asize->expr.args[0]->lit.intval; ++i) {
+				classify_recursive(t->sub[0], p, total_offset);
+			}
+		}
+	}
+
+	*total_offset = align(cur_offset + sz, tyalign(t));
+}
+
+void
+classify(Type *t, PassIn *p)
+{
+	size_t total_offset = 0;
+	/* p must be of length exactly 2 */
+	p[0] = PassInNoPref;
+	p[1] = PassInNoPref;
+	classify_recursive(t, p, &total_offset);
+}
+
 int
 isaggregate(Type *t)
 {
@@ -414,4 +547,32 @@
 	t = tybase(t);
 	return (t->type == Tystruct || t->type == Tyarray || t->type == Tytuple ||
 		(t->type == Tyunion && !isenum(t)));
+}
+
+RetType howreturned(Type *t)
+{
+	/*
+	 * This is only for determining how values are returned from functions.
+	 * Determining how arguments are passed requires register counting using
+	 * the whole prototype.
+	 */
+	size_t sz = tysize(t);
+	PassIn pc[2] = { PassInNoPref, PassInNoPref };
+
+	if (tybase(t)->type == Tyvoid) {
+		return RetVoid;
+	} else if (isstacktype(t)) {
+		if (isaggregate(t) && sz <= 16) {
+			classify(t, pc);
+			if (pc[0] == PassInMemory || pc[1] == PassInMemory) {
+				return RetBig;
+			}
+
+			return RetSmallAggregate;
+		}
+
+		return RetBig;
+	}
+
+	return RetReg;
 }