-
-
Notifications
You must be signed in to change notification settings - Fork 606
/
argtypes_aarch64.d
194 lines (168 loc) · 6.06 KB
/
argtypes_aarch64.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
* Break down a D type into basic (register) types for the AArch64 ABI.
*
* Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
* Authors: Martin Kinkelin
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_aarch64.d, _argtypes_aarch64.d)
* Documentation: https://dlang.org/phobos/dmd_argtypes_aarch64.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_aarch64.d
*/
module dmd.argtypes_aarch64;
import dmd.astenums;
import dmd.mtype;
/****************************************************
* This breaks a type down into 'simpler' types that can be passed to a function
* in registers, and returned in registers.
* This is the implementation for the AAPCS64 ABI, based on
* https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst.
* Params:
* t = type to break down
* Returns:
* tuple of 1 type if `t` can be passed in registers; e.g., a static array
* for Homogeneous Floating-point/Vector Aggregates (HFVA).
* A tuple of zero length means the type cannot be passed/returned in registers.
* null indicates a `void`.
*/
extern (C++) TypeTuple toArgTypes_aarch64(Type t)
{
if (t == Type.terror)
return new TypeTuple(t);
const size = cast(size_t) t.size();
if (size == 0)
return null;
Type tb = t.toBasetype();
const isAggregate = tb.ty == Tstruct || tb.ty == Tsarray || tb.ty == Tarray || tb.ty == Tdelegate || tb.iscomplex();
if (!isAggregate)
return new TypeTuple(t);
Type hfvaType;
enum maxNumHFVAElements = 4;
const isHFVA = size > maxNumHFVAElements * 16 ? false : isHFVA(tb, maxNumHFVAElements, &hfvaType);
// non-PODs and larger non-HFVA PODs are passed indirectly by value (pointer to caller-allocated copy)
if ((size > 16 && !isHFVA) || !isPOD(tb))
return TypeTuple.empty;
if (isHFVA)
{
// pass in SIMD registers
return new TypeTuple(hfvaType);
}
// pass remaining aggregates in 1 or 2 GP registers
static Type getGPType(size_t size)
{
switch (size)
{
case 1: return Type.tint8;
case 2: return Type.tint16;
case 4: return Type.tint32;
case 8: return Type.tint64;
default: return Type.tint64.sarrayOf((size + 7) / 8);
}
}
return new TypeTuple(getGPType(size));
}
/**
* A Homogeneous Floating-point/Vector Aggregate (HFA/HVA) is an ARM/AArch64
* concept that consists of up to 4 elements of the same floating point/vector
* type. It is the aggregate final data layout that matters so structs, unions,
* static arrays and complex numbers can result in an HFVA.
*
* simple HFAs: struct F1 {float f;} struct D4 {double a,b,c,d;}
* interesting HFA: struct {F1[2] vals; float weight;}
*
* If the type is an HFVA and `rewriteType` is specified, it is set to a
* corresponding static array type.
*/
extern (C++) bool isHFVA(Type t, int maxNumElements = 4, Type* rewriteType = null)
{
t = t.toBasetype();
if ((t.ty != Tstruct && t.ty != Tsarray && !t.iscomplex()) || !isPOD(t))
return false;
Type fundamentalType;
const N = getNestedHFVA(t, fundamentalType);
if (N < 1 || N > maxNumElements)
return false;
if (rewriteType)
*rewriteType = fundamentalType.sarrayOf(N);
return true;
}
private:
bool isPOD(Type t)
{
auto baseType = t.baseElemOf();
if (auto ts = baseType.isTypeStruct())
return ts.sym.isPOD();
return true;
}
/**
* Recursive helper.
* Returns size_t.max if the type isn't suited as HFVA (element) or incompatible
* to the specified fundamental type, otherwise the number of consumed elements
* of that fundamental type.
* If `fundamentalType` is null, it is set on the first occasion and then left
* untouched.
*/
size_t getNestedHFVA(Type t, ref Type fundamentalType)
{
t = t.toBasetype();
if (auto tarray = t.isTypeSArray())
{
const N = getNestedHFVA(tarray.nextOf(), fundamentalType);
return N == size_t.max ? N : N * cast(size_t) tarray.dim.toUInteger(); // => T[0] may return 0
}
if (auto tstruct = t.isTypeStruct())
{
// check each field recursively and set fundamentalType
bool isEmpty = true;
foreach (field; tstruct.sym.fields)
{
const field_N = getNestedHFVA(field.type, fundamentalType);
if (field_N == size_t.max)
return field_N;
if (field_N > 0) // might be 0 for empty static array
isEmpty = false;
}
// an empty struct (no fields or only empty static arrays) is an undefined
// byte, i.e., no HFVA
if (isEmpty)
return size_t.max;
// due to possibly overlapping fields (for unions and nested anonymous
// unions), use the overall struct size to determine N
const structSize = t.size();
const fundamentalSize = fundamentalType.size();
assert(structSize % fundamentalSize == 0);
return cast(size_t) (structSize / fundamentalSize);
}
Type thisFundamentalType;
size_t N;
if (t.isTypeVector())
{
thisFundamentalType = t;
N = 1;
}
else if (t.isfloating()) // incl. imaginary and complex
{
auto ftSize = t.size();
N = 1;
if (t.iscomplex())
{
ftSize /= 2;
N = 2;
}
switch (ftSize)
{
case 4: thisFundamentalType = Type.tfloat32; break;
case 8: thisFundamentalType = Type.tfloat64; break;
case 16: thisFundamentalType = Type.tfloat80; break; // IEEE quadruple
default: assert(0, "unexpected floating-point type size");
}
}
else
{
return size_t.max; // reject all other types
}
if (!fundamentalType)
fundamentalType = thisFundamentalType; // initialize
else if (fundamentalType != thisFundamentalType)
return size_t.max; // incompatible fundamental types, reject
return N;
}