ref: dfc145534768f3b461d92c929931bb3b48237bdb
parent: cba8dceb7246552cc446b60cef4d5a3069907faf
author: S. Gilles <[email protected]>
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;
}