[PATCH 4/9] Add classification algorithm for small-struct passing.
[Thread Prev] | [Thread Next]
- Subject: [PATCH 4/9] Add classification algorithm for small-struct passing.
- From: "S. Gilles" <sgilles@xxxxxxx>
- Reply-to: myrddin-dev@xxxxxxxxxxxxxx
- Date: Sat, 20 Jun 2020 22:40:04 -0400
- To: "myrddin-dev" <myrddin-dev@xxxxxxxxxxxxxx>
- Cc: "S. Gilles" <sgilles@xxxxxxx>
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#).
---
6/asm.h | 17 ++++++
6/isel.c | 16 -----
6/typeinfo.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 178 insertions(+), 16 deletions(-)
diff --git a/6/asm.h b/6/asm.h
index 04c088ed..447ee7ac 100644
--- a/6/asm.h
+++ b/6/asm.h
@@ -45,6 +45,20 @@ typedef enum {
Nmode,
} Mode;
+typedef enum {
+ PassInNoPref,
+ PassInSSE,
+ PassInInt,
+ PassInMemory,
+} PassIn;
+
+typedef enum {
+ RetVoid,
+ RetReg,
+ RetSmallAggregate,
+ RetBig,
+} RetType;
+
typedef enum {
Classbad,
Classint,
@@ -316,7 +330,10 @@ size_t tyalign(Type *t);
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();
diff --git a/6/isel.c b/6/isel.c
index 68eeb8dd..3dc139b9 100644
--- a/6/isel.c
+++ b/6/isel.c
@@ -500,22 +500,6 @@ call(Isel *s, Node *n)
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)
{
diff --git a/6/typeinfo.c b/6/typeinfo.c
index cc12f4a7..aff1c338 100644
--- a/6/typeinfo.c
+++ b/6/typeinfo.c
@@ -408,6 +408,139 @@ offset(Node *aggr, Node *memb)
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)
{
@@ -415,3 +548,31 @@ isaggregate(Type *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;
+}
--
2.27.0
| [PATCH 0/9] v2: Handle small-aggregates via AMD64 abi | "S. Gilles" <sgilles@xxxxxxx> |
- Prev by Date: [PATCH 3/9] Factor out the code for placing/retrieving arguments.
- Next by Date: [PATCH 5/9] Pass small-aggregate arguments by the AMD64 abi.
- Previous by thread: [PATCH 3/9] Factor out the code for placing/retrieving arguments.
- Next by thread: [PATCH 5/9] Pass small-aggregate arguments by the AMD64 abi.
- Index(es):