[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):