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;
}
--
⑨